Hbase的热点问题
热点问题的产生
当大量的客户端访问定向到集群中的一个节点或者几个节点时,就会导致热点问题。此访问操作可能是写入,也可能是读取。大量的访问使得管理该region的计算机不堪重负,从而导致性能下降,并可能导致region的不可用。这也可能对同一RegionServer管理的其他region产生不利影响。
热点问题的解决
采用预分区的方式,解决热点问题
每一个region都维护着一个startRowKey和一个endRowKey,如果加入的范围符合某个region维护的rowkey范围,那么该数据交给这个region来维护,依照这个原则,我们可以将数据所要投放的分区提前大致规划好,提高Hbase的性能。
create 'mydb:table','base_info',SPLITS=>['1000','2000','3000','4000']
上述的预分区,将一个region分成了五部分
StartKey EndKey
1000
1000 2000
2000 3000
3000 4000
4000
可以插入条数据进入不同的分区:
put 'mydb:table','0001','base_info:name','zhangsan'
put 'mydb:table','1001','base_info:name','lisi'
put 'mydb:table','2001','base_info:name','wangwu'
put 'mydb:table','3001','base_info:name','zhaoliu'
put 'mydb:table','4001','base_info:name','zhouqi'
上述所示,再插入数据的时候,指定分区,可将数据插入自己想要插入的分区。
从热点问题中,不难看出,在Hbase中RowKey的重要性。接下来,一起来看一看RowKey的设计原则
Hbase中RowKey的设计原则
Hbase由于其读写和存储的高性能,在OLAP分析中占据着越来越重要的地位,作为非关系型数据库的一员,Hbase在查询数据时,只有两种方式:
a) 通过RowKey进行查询(RowKey用来表示唯一一行记录)
b) 全表扫描,再结合过滤器筛选出目标数据,缺点是性能太低。
设计原则
唯一原则
在Hbase中,数据的存储时Key-Value的形式,若Hbase中同一张表,插入了相同的rowkey,并且是同一个列簇下的同一个key时,如果version设置为1的话,则原先的数据会被覆盖掉,所以务必保证Rowkey的唯一性。
长度原则
RowKey是一个二进制的码流,RowKey的长度被认为在10-100个字节,不过建议是越短越好,尽量不超过16个字节。
RowKey是全局唯一的,是有序的,所以查询RowKey比全表查询的效率快得多
-1. 数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率;
-2. MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey的字节长度越短越好。
-3. 目前操作系统是都是64位系统,内存8字节对齐。控制在16个字节,8字节的整数倍利用操作系统的最佳特性。
需要指出的是不仅Rowkey的长度是越短越好,而且列族名、列名等尽量使用短名字,因为HBase属于列式数据库,这些名字都是会写入到HBase的持久化文件HFile中去,过长的Rowkey、列族、列名都会导致整体的存储量成倍增加。
散列原则
我们设计的rowkey应该均匀的分布在各个Hbase节点上。
拿常见的时间戳举例,假如Rowkey是按系统时间戳的方式递增,Rowkey的第一部分如果是时间戳信息的话将造成所有新数据都在一个RegionServer上堆积的热点现象,就是region热点问题。
我们可以采取下面三种方式,来解决这个Region热点问题:
1、Reverse反转
针对固定长度的RowKey反转后存储,这样可以使RowKey中经常改变的部分放在最前面,可以有效地随机rowkey。
反转Rowkey的例子通常以手机举例,可以将手机号反转后的字符串作为Rowkey,这样的就避免了以手机号那样比较固定开头(137x、15x等)导致热点问题,这样做的缺点是牺牲了Rowkey的有序性。
2、Salting加盐
Salting是将每一个RowKey加一个前缀,前缀使用一些随机字符,使得数据分散在不同的Region,达到Region负载均衡的目标。
3、Hash散列或者Mod
用Hash散列来代替随机Salt前缀的好处是能让一个给定的行有相同的前缀,这在分散RowKey负载的同时,使读操作能够推断。确定性Hash(比如md5后取前4位做前缀)能让客户端重建完整的RowKey,可以使用get操作直接get想要的行。
例如将上述的原始Rowkey经过hash处理,此处我们采用md5散列算法取前4位做前缀,结果如下
9bf0-abc001 (abc001在md5后是9bf049097142c168c38a94c626eddf3d,取前4位是9bf0)
7006-abc002
95e6-abc003
若用前4个字符作为不同分区的起止,上面几个Rowkey数据会分布在3个region中。实际应用场景是当数据量越来越大的时候,这种设计会使得分区之间更加均衡。
如果Rowkey是数字类型的,也可以考虑Mod方法。
Hbase的调优
服务端调优
1. hbase.regionserver.handler.count:rpc请求的线程数量,默认值是10,生产环境建议使用100,
也不是越大越好,特别是当请求内容很大的时候,比如scan/put几M的数据,会占用过多的内存,
有可能导致频繁的GC,甚至出现内存溢出。
2. hbase.master.distributed.log.splitting:默认值为true,建议设为false。关闭hbase的分布
式日志切割,在log需要replay时,由master来负责重放
3. hbase.regionserver.hlog.splitlog.writer.threads:默认值是3,建议设为10,日志切割所用
的线程数
4. hbase.snapshot.enabled:快照功能,默认是false(不开启),建议设为true,特别是对某些关键
的表,定时用快照做备份是一个不错的选择。
5. hbase.hregion.max.filesize:默认是10G, 如果任何一个column familiy里的StoreFile超过
这个值, 那么这个Region会一分为二,因为region分裂会有短暂的region下线时间(通常在5s以内),
为减少对业务端的影响,建议手动定时分裂,可以设置为60G。
6. hbase.hregion.majorcompaction:hbase的region主合并的间隔时间,默认为1天,建议设置为0,
禁止自动的major主合并,major合并会把一个store下所有的storefile重写为一个storefile文件,在
合并过程中还会把有删除标识的数据删除,在生产集群中,主合并能持续数小时之久,为减少对业务的影
响,建议在业务低峰期进行手动或者通过脚本或者api定期进行major合并。
7. hbase.hregion.memstore.flush.size:默认值128M,单位字节,一旦有memstore超过该值将被
flush,如果regionserver的jvm内存比较充足(16G以上),可以调整为256M。
8. hbase.hregion.memstore.block.multiplier:默认值4,如果一个memstore的内存大小已经超过
hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier,则会阻塞
该memstore的写操作,为避免阻塞,建议设置为5,如果太大,则会有OOM的风险。如果在
regionserver日志中出现"Blocking updates for '<threadName>' on region <regionName> :
memstore size <多少M> is >= than blocking <多少M> size"的信息时,说明这个值该调整了。
9. hbase.hstore.compaction.min:默认值为3,如果任何一个store里的storefile总数超过该值,
会触发默认的合并操作,可以设置5~8,在手动的定期major compact中进行storefile文件的合并,减
少合并的次数,不过这会延长合并的时间,以前的对应参数为hbase.hstore.compactionThreshold。
10. hbase.hstore.compaction.max:默认值为10,一次最多合并多少个storefile,避免OOM。
11. hbase.hstore.blockingStoreFiles:默认为7,如果任何一个store(非.META.表里的store)的
storefile的文件数大于该值,则在flush memstore前先进行split或者compact,同时把该region添加
到flushQueue,延时刷新,这期间会阻塞写操作直到compact完成或者超过hbase.hstore.blockingWaitTime
(默认90s)配置的时间,可以设置为30,避免memstore不及时flush。当regionserver运行日志中出现
大量的“Region <regionName> has too many store files; delaying flush up to 90000ms"时,
说明这个值需要调整了。
12. hbase.regionserver.global.memstore.upperLimit:默认值0.4,regionserver所有memstore
占用内存在总内存中的upper比例,当达到该值,则会从整个regionserver中找出最需要flush的region
进行flush,直到总内存比例降到该数以下,采用默认值即可。
13. hbase.regionserver.global.memstore.lowerLimit:默认值0.35,采用默认值即可。
14. hbase.regionserver.thread.compaction.small:默认值为1,regionserver做Minor Compaction
时线程池里线程数目,可以设置为5。
15. hbase.regionserver.thread.compaction.large:默认值为1,regionserver做Major Compaction
时线程池里线程数目,可以设置为8。
16. hbase.regionserver.lease.period:默认值60000(60s),客户端连接regionserver的租约超时
时间,客户端必须在这个时间内汇报,否则则认为客户端已死掉。这个最好根据实际业务情况进行调整
17. hfile.block.cache.size:默认值0.25,regionserver的block cache的内存大小限制,在偏向
读的业务中,可以适当调大该值,需要注意的是hbase.regionserver.global.memstore.upperLimit的
值和hfile.block.cache.size的值之和必须小于0.8。
18. dfs.socket.timeout:默认值60000(60s),建议根据实际regionserver的日志监控发现了异常进
行合理的设置,比如我们设为900000,这个参数的修改需要同时更改hdfs-site.xml
19. dfs.datanode.socket.write.timeout:默认480000(480s),有时regionserver做合并时,可能
会出现datanode写超时的情况,480000 millis timeout while waiting for channel to be ready
for write,这个参数的修改需要同时更改hdfs-site.xml
客户端调优
1.hbase.client.write.buffer:默认为2M,写缓存大小,推荐设置为5M,单位是字节,当然越大占用
的内存越多,此外测试过设为10M下的入库性能,反而没有5M好
2.hbase.client.pause:默认是1000(1s),如果你希望低延时的读或者写,建议设为200,这个值通常
用于失败重试,region寻找等
3.hbase.client.retries.number:默认值是10,客户端最多重试次数,可以设为11,结合上面的参数,
共重试时间71s
4.hbase.ipc.client.tcpnodelay:默认是false,建议设为true,关闭消息缓冲
5.hbase.client.scanner.caching:scan缓存,默认为1,避免占用过多的client和regionServer的
内存,一般1000以内合理,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次
查询的数据条数,如果是扫描数据对下次查询没有帮助,则可以设置scan的setCacheBlocks为false,
避免使用缓存;
6.table用完需关闭,关闭scanner
7.限定扫描范围:指定列簇或者指定要查询的列,指定startRow和endRow
8.使用Filter可大量减少网络消耗
9.通过Java多线程入库和查询,并控制超时时间。
10.建表注意事项:
开启压缩
合理的设计rowkey
进行预分区
开启bloomfilter
来源:CSDN
作者:是微光啊
链接:https://blog.csdn.net/Aut1sm/article/details/104745318