一.概述
再次学习HBase实战和HBase权威指南时,对HBase了解又深了许多。本文列出一些值得关注的点。
二.HBase物理和逻辑存储结构
user表包含两个列族info activity,为此表预分区[1,3) [3,正无穷),此时user表在HBase里的物理和逻辑存储结构如上图。
- 一个user表的数据存储在两个region上,这两个region可能在一个RegionServer上,也可能在两个不同的RegionServer上。
- 一个RegionServer可以有多个region,这些region共享一个WAL(write-ahead log预写式日志,也称HLog)。
- 一个region可以包含多个列族的数据。
- 一个列族由一个MemStore,一个BlockCache和多个HFile组成。两个不同列族的MemStore BlockCache 毫无关系。
- 也有书里谈到Store这个概念,实际就是对于一个列族的管辖区域。
三.HBase读写路径
- 执行写入时会写到两个地方:WAL和MemStore,只有当这两个地方的变化信息都被成功写入并确认后,才认为写动作完成。MemStore填满(hbase.hregion.memstore.flush.size默认64M,在hbase0.96 habse-default里为128M)后被flush到硬盘,所以客户端的写操作不会与底层的HFile直接交互。
- 从HBase中读出一行,首先会检查MemStore等待被flush的数据,其次检查BlockCache是否包含该行的Block信息,最后访问硬盘上的HFile。BlockCache使用LRU算法,缓存的是Block块而非Block所有。
四.客户端写缓冲区
- 每一个put操作实际上都是一个RPC操作。当然,HBase的API配备了一个写缓冲区,它负责收集put请求,然后调用RPC操作一次性将put发送给服务器。
- 注意这个缓冲区位于客户端,默认情况下是禁用的。HTable的setAutoFlush(false)方法可以将自动刷新autoflush设置为false来激活缓冲区。
@Override public boolean isAutoFlush() { return autoFlush; } Deprecated @Override public void setAutoFlush(boolean autoFlush) { setAutoFlush(autoFlush, autoFlush); } /** * {@inheritDoc} */ @Override public void setAutoFlushTo(boolean autoFlush) { setAutoFlush(autoFlush, clearBufferOnFail); } /** * {@inheritDoc} */ @Override public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) { this.autoFlush = autoFlush; this.clearBufferOnFail = autoFlush || clearBufferOnFail; }
- 也可以配置此缓冲区大小。默认大小为2MB。
@Override public long getWriteBufferSize() { } public void setWriteBufferSize(long writeBufferSize) throws IOException { }
- 当需要强制把数据写到服务器时,使用
@Override public void flushCommits() throws InterruptedIOException, RetriesExhaustedWithDetailsException { // As we can have an operation in progress even if the buffer is empty, we call // backgroundFlushCommits at least one time. }
- 此缓冲区没有任何备份,这些数据可能丢失。
五.大小合并 自动分区
首先要明白一个概念,大小合并是针对HFile,自动分区是针对region。
- 当一个region里的存储文件增长到大于配置的hbase.hregion.max.filesize默认256M时,region会被一分为二。关于分区的过程,我还没弄太明白。
- 大小合并
- 小合并:
- 将多个HFile合并成一个HFile。经过读-合并写-删除步骤。
- 小合并的文件数量由hbase.hstore.compaction.min默认3且要求大于等于2和hbase.hstore.compaction.max默认10决定。
- 小合并的文件大小由hbase.hstore.compaction.min.size默认64M和hbase.hstore.compaction.max.size默认Long.MAX_VALUE决定。hase.hstore.compaction.min.size比较奇葩,小于这个值才被include。既然这样,hbase.hstore.compaction.max.si还有何用?
- 大合并:
- 将给定region的一个列族的所有HFile合并成一个HFile。
- 大合并是HBase清理被删除的唯一机会。如果一个单元的版本超过了最大数量,也会在大合并时删掉。
- 触发大合并:shell命令或客户端API;距离上次大合并是否超过hbase.hregion.majorcompaction默认24小时;小合并的文件就是一个列族的所有文件且满足文件大合并大小要求,小合并被提升为大合并。
- 小合并:
六.HFile
- 我们已经知道HFile的两种来源:由MemStore flush而来,由其它HFile合并而来。所以我们并不知道HFile到底有多大,实际上我们也不需要知道。需要了解的是,HFile只是一个逻辑上的概念,真正存储的是HDFS,而HFile与HDFS的块之间没有匹配关系,HBase把文件透明的存储在HDFS上,如一个HFile232M,将被存储在两个Block上,Block1 128M,Block2 104M。
- HFile块大小64KB怎么理解?先看一个HFile的结构。
这里的块指的就是Data块(一系列KeyValue的集合),也就是一个HFile由许多大小为64KB的Data块以及其它项组成。
- KeyValue的结构
这个比较好理解。通过此图可以看到,如果一个rowkey存在多个版本,每个版本都将对应一个KeyValue,因为它们TimeStamp不同。
当然HBase还有许多值得深究的地方,后面会不断学习不断更新本文。如有错误,欢迎指出。