Region Server
Region Server 为 Region 的管理者,其实现类为 HRegionServer,主要作用如下:
Master
Master 是所有 Region Server 的管理者,其实现类为 HMaster,主要作用如下:
Zookeeper
HBase 通过 Zookeeper 来做 Master 的高可用、RegionServer 的监控、元数据的入口以及集群配置的维护等工作。
HDFS
HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持。
每个RegionServer 可以服务多个Region,每个RegionServer中有多个Store、1个WAL,和1个BlockCache,每个Store对应一个列族,包含MemStore 和 StoreFile.
说明:写入数据时,逻辑顺序是先写入到WAL(HLog),再写入memStore,但实际源码流程如下:使用事务回滚机制来确保WAL和memStore同步写入。
第一种触发条件:(Region级别Flush)
<!-- 单个region里memstore的缓存大小,超过那么整个HRegion就会flush,默认128M -->
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
<description>
Memstore will be flushed to disk if size of the memstore
exceeds this number of bytes. Value is checked by a thread that runs
every hbase.server.thread.wakefrequency.
</description>
</property>
<!-- 当一个HRegion上的memstore的大小满足hbase.hregion.memstore.block.multiplier *
hbase.hregion.memstore.flush.size, 这个HRegion会执行flush操作并阻塞对该HRegion的写入 -->
<property>
<name>hbase.hregion.memstore.block.multiplier</name>
<value>4</value>
<description>
Block updates if memstore has hbase.hregion.memstore.block.multiplier
times hbase.hregion.memstore.flush.size bytes. Useful preventing
runaway memstore during spikes in update traffic. Without an
upper-bound, memstore fills such that when it flushes the
resultant flush files take a long time to compact or split, or
worse, we OOME.
</description>
</property>
第二种触发条件:(RegionServer级别Flush)
<!-- regionServer的全局memstore的大小,超过该大小会触发flush到磁盘的操作,默认是堆大小的40%,而且regionserver级别的
flush会阻塞客户端读写。如果RegionServer的堆内存设置为2G,则memstore总内存大小为 2 * 0.4 = 0.8G-->
<property>
<name>hbase.regionserver.global.memstore.size</name>
<value></value>
<description>Maximum size of all memstores in a region server before
new
updates are blocked and flushes are forced. Defaults to 40% of heap (0.4).
Updates are blocked and flushes are forced until size of all
memstores
in a region server hits
hbase.regionserver.global.memstore.size.lower.limit.
The default value in this configuration has been intentionally left
emtpy in order to
honor the old hbase.regionserver.global.memstore.upperLimit property if
present.
</description>
</property>
<!--可以理解为一个安全的设置,有时候集群的“写负载”非常高,写入量一直超过flush的量,这时,我们就希望memstore不要超过一定的安全设置。
在这种情况下,写操作就要被阻塞一直到memstore恢复到一个“可管理”的大小, 这个大小就是默认值是堆大小 * 0.4 * 0.95,也就是当regionserver级别
的flush操作发送后,会阻塞客户端写,一直阻塞到整个regionserver级别的memstore的大小为 堆大小 * 0.4 *0.95为止 -->
<property>
<name>hbase.regionserver.global.memstore.size.lower.limit</name>
<value></value>
<description>Maximum size of all memstores in a region server before
flushes are forced.
Defaults to 95% of hbase.regionserver.global.memstore.size (0.95).
A 100% value for this value causes the minimum possible flushing to
occur when updates are
blocked due to memstore limiting.
The default value in this configuration has been intentionally left
emtpy in order to
honor the old hbase.regionserver.global.memstore.lowerLimit property if
present.
</description>
</property>
第三种触发条件:(RegionServer级别Flush)
第四种触发条件:(Region级别Flush)
#region的memstore的触发
#判断如果某个region中的某个memstore达到这个阈值,那么触发flush,flush这个region的所有memstore
hbase.hregion.memstore.flush.size=128M
#region的触发级别:如果没有memstore达到128,但是所有memstore的大小加在一起大于等于128*4
#触发整个region的flush
hbase.hregion.memstore.block.multiplier=4
#regionserver的触发级别:所有region所占用的memstore达到阈值,就会触发整个regionserver中memstore的溢写
#从memstore占用最多的Regin开始flush
hbase.regionserver.global.memstore.size=0.4 --RegionServer中Memstore的总大小
#低于水位后停止
hbase.regionserver.global.memstore.size.upper.limit=0.99
hbase.regionserver.global.memstore.size.lower.limit = 0.4*0.95 =0.38
#设置了一个flush的最小阈值
#memstore的判断发生了改变:max("hbase.hregion.memstore.flush.size / column_family_number",hbase.hregion.percolumnfamilyflush.size.lower.bound.min)
#如果memstore高于上面这个结果,就会被flush,如果低于这个值,就不flush,如果整个region所有的memstore都低于,全部flush
#水位线 = max(128 / 列族个数,16),列族一般给3个 ~ 42M
#如果memstore的空间大于42,就flush,如果小于就不flush;如果都小于,全部flush
举例:3个列族,3个memstore, 90/30/30 90会被Flush
举例:3个列族,3个memstore, 30/30/30 全部flush
hbase.hregion.percolumnfamilyflush.size.lower.bound.min=16M
# 2.x中多了一种机制:In-Memory-compact,如果开启了【不为none】,会在内存中对需要flush的数据进行合并
#合并后再进行flush,将多个小文件在内存中合并后再flush
hbase.hregion.compacting.memstore.type=none|basic|eager|adaptive
hbase.hregion.memstore.flush.size
默认值 128M,单个 MemStore 大小超过该阈值就会触发 Flush。如果当前集群 Flush 比较频繁,并且内存资源比较充裕,建议适当调整为 256M。调大的副作用可能是造成宕机时需要分裂的 HLog 数量变多,从而延长故障恢复时间。
hbase.hregion.memstore.block.multiplier
默认值 4,Region 中所有 MemStore 超过单个 MemStore 大小的倍数达到该参数值时,就会阻塞写请求并强制 Flush。一般不建议调整,但对于写入过快且内存充裕的场景,为避免写阻塞,可以适当调整到5~8。
hbase.regionserver.global.memstore.size
默认值 0.4,RegionServer 中所有 MemStore 大小总和最多占 RegionServer 堆内存的 40%。这是写缓存的总比例,可以根据实际场景适当调整,且要与 HBase 读缓存参数 hfile.block.cache.size(默认也是0.4)配合调整。旧版本参数名称为 hbase.regionserver.global.memstore.upperLimit。
hbase.regionserver.global.memstore.size.lower.limit
默认值 0.95,表示 RegionServer 中所有 MemStore 大小的低水位是 hbase.regionserver.global.memstore.size 的 95%,超过该比例就会强制 Flush。一般不建议调整。旧版本参数名称为 hbase.regionserver.global.memstore.lowerLimit。
hbase.regionserver.optionalcacheflushinterval
默认值 3600000(即 1 小时),HBase 定期 Flush 所有 MemStore 的时间间隔。一般建议调大,比如 10 小时,因为很多场景下 1 小时 Flush 一次会产生很多小文件,一方面导致 Flush 比较频繁,另一方面导致小文件很多,影响随机读性能,因此建议设置较大值。
此外还有一点值得说一下
官方建议使用一个列族,因为一个列族就对应磁盘中的一个文件,如果使用多个列族,数据不均匀的情况下,在全局Flush时会产生较多的小文件。
如: 列族 CF1 CF2 CF3
100M 1K 5K
全局Flush时,对于CF2和CF3会生成2个小文件。但是也可以使用多个列族,尽量保证数据均匀就好。
细节
HBase 在实现中提供了两种缓存结构 MemStore(写缓存) 和 BlockCache(读缓存)。写缓存前面说过不再重复。
重点
读数据时不要理解为先从 MemStore 中读取,读不到再读 BlockCache 中,还读不到再从HFile中读取,然后将数据写入到 BlockCache 中。因为如果人为设置导致磁盘数据new,内存数据old。你读取的时候会出错的!
结论
HBase 把磁盘跟内存数据一起读,然后把磁盘数据放到 BlockCache中,BlockCache 是磁盘数据的缓存。HBase 是个读比写慢的工具。
由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。
为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。
# 2.0版本之前,只有StoreFile文件的合并
磁盘中合并:minor compaction、major compaction
# 2.0版本开始,内存中的数据也可以先合并后Flush
内存中合并:In-memory compaction
磁盘中合并:minor compaction、major compaction
Minor Compaction :轻量级
minor compact文件选择标准由以下几个参数共同决定
<!--待合并文件数据必须大于等于下面这个值-->
<property>
<name>hbase.hstore.compaction.min</name>
<value>3</value>
</property>
<!--待合并文件数据必须小于等于下面这个值-->
<property>
<name>hbase.hstore.compaction.max</name>
<value>10</value>
</property>
<!--默认值为128m,
表示文件大小小于该值的store file 一定会加入到minor compaction的store file中
-->
<property>
<name>hbase.hstore.compaction.min.size</name>
<value>134217728</value>
</property>
<!--默认值为LONG.MAX_VALUE,
表示文件大小大于该值的store file 一定会被minor compaction排除-->
<property>
<name>hbase.hstore.compaction.max.size</name>
<value>9223372036854775807</value>
</property>
Major Compaction :重量级合并
In-memory compaction
禁用自动合并,使用手动处理:
Compact all regions in a table:
hbase> major_compact 't1'
hbase> major_compact 'ns1:t1'
Compact an entire region:
hbase> major_compact 'r1'
Compact a single column family within a region:
hbase> major_compact 'r1', 'c1'
概括的说,HBase 会在三种情况下检查是否要触发 Compaction,分别是 MemStore Flush、后台线程周期性检查、手动触发。
hbase.hstore.compaction.min
hbase.hstore.compaction.max
hbase.regionserver.thread.compaction.throttle
hbase.regionserver.thread.compaction.large/small
hbase.hstore.blockingStoreFiles
<!-- 每个store阻塞更新请求的阀值,表示如果当前hstore中文件数大于该值,系统将会强制执行compaction操作进行文件合并,
合并的过程会阻塞整个hstore的写入,这样有个好处是避免compaction操作赶不上Hfile文件的生成速率 -->
<property>
<name>hbase.hstore.blockingStoreFiles</name>
<value>10</value>
<description>
If more than this number of StoreFiles in any one Store
(one StoreFile is written per flush of MemStore) then updates are
blocked for this HRegion until a compaction is completed, or
until hbase.hstore.blockingWaitTime has been exceeded.
</description>
</property>
默认值 10,表示一个 Store 中 HFile 文件数量达到该值就会阻塞写入,等待 Compaction 的完成。一般建议调大点,比如设置为 100,避免出现阻塞更新的情况,阻塞日志如下:
too many store files; delaying flush up to 90000ms
生产环境建议认真根据实际业务量做好集群规模评估,如果小集群遇到了持续写入过快的场景,合理扩展集群也非常重要。
默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。
Region Split 时机
数据切分会造成数据倾斜(region 大小分布不均),带来热点数据问题。所以建表时进行预分区来尽量避免这种问题。
关闭自动分裂,手动执行
split 'tableName'
split 'namespace:tableName'
split 'regionName' # format: 'tableName,startKey,id'
split 'tableName', 'splitKey'
split 'regionName', 'splitKey'
被删除数据会在两种操作中被清除:
为什么 Flush 不清除 Delete 标记?
因为 Flush 只能清除 memStore 中的数据,还要留着 Delete 标记供 major compact 合并时去清除其他 StoreFile 中的数据。
唯一原则:Rowkey必须具有唯一性,不能重复,一个Rowkey唯一标识一条数据
业务原则:Rowkey的设计必须贴合业务的需求,一般选择最常用的查询条件作为rowkey的前缀
组合原则:将更多的经常作为的查询条件的列放入Rowkey中,可以满足更多的条件查询可以走索引查询
散列原则:为了避免出现热点问题,需要将数据的rowkey生成规则构建散列的rowkey
长度原则:在满足业务需求情况下,rowkey越短越好,一般建议Rowkey的长度小于100字节
ROW:行级布隆过滤
ROWCOL:行列级布隆过滤
系统环境
HDFS
zookeeper
hbase
参考:
https://www.cnblogs.com/wdh01/p/16283244.html
https://blog.csdn.net/qq_33208851/article/details/105223419
https://blog.csdn.net/weixin_44133605/article/details/124635627
https://www.cnblogs.com/baran/p/15629009.html
https://www.cnblogs.com/sx66/p/13425465.html
https://blog.csdn.net/zfw_666666/article/details/126627977
https://blog.csdn.net/llAl_lAll/article/details/122260638
https://blog.csdn.net/weixin_48370579/article/details/125802173