小记--------hbase数据库预分区设计,rowkey设计及参数调优
- 表的设计
1.1预分区设计
每一个region维护者startrowkey与endrowkey,如果假如的数据符合某个region维护的rowkey范围,则该数据交给这个region维护,所以我们可以将数据所有投放的分区提前大致的规划好,以提高hbase性能
可以通过hbase shell 命令进行创建
create 't9' , 'MM' , SPLITS => ['30000' , '60000' , '90000' ]
create 't4' , { NAME => 'MM' , COMPRESSION => 'snappy' } , {NUMREGIONS => 5 , SPLITALGO => 'HexStringSplit'}
通过javaapi创建
调用HBASEAdmin类的createTable方法进行创建
1.2 rowkey设计
首先需要考虑应用场景的读写比列:重写轻读,重读轻写,读写一样
因为数据负载均衡和高效的读取时常是矛盾的
1.在重写轻读的大数据场景中:rowkey的设计应在满足基本查询需求的前提下,更注重整体的吞吐量
2.在重读轻写的大数据场景中:rowkey的设计则应更侧重于如何高效的读取;
rowkey字段的选取
唯一性:rowkey必须能够保证唯一的识别一行数据;不论应用什么样的负载均衡场景,rowkey字段应该参考最高频的查询场景
长度原则:rowkey的字段应控制在16个字节之内(在大多数的场景中),从而避免因rowkey字段太长影响Hfile的存储效率
A.避免热点的方法1 -Reversing (反转)
如果rowkey 本身分布均匀,但是rowkey的尾部数据缺是比较好的随机性,此时,可以考虑将rowkey的信息反转,或者直接将尾部的数据提到rowkey的前部1001 1002 5个
缺点:场景利于查询中的get但是不利于scan,因为数据在原rowkey上的自然顺序已经被打乱
B.避免热点的方法2: - Salting (前部添加随机数)
Salting的原理实在原rowkey的前面添加固定长度的随机bytes,随机bytes可以保证数据在所有的region上是负载均衡的
缺点:因为是随机的bytes ,所以原rowkey查询时无法获知随机bytes信息,所以在读取过程中需要倒各个可能的region中查询。所以Salting对于读取是不利的
C.避免数据热点的方法3 - Hashing
基于rowkey的全部或部分数据进行hash,而后将hashing后的值完整替代原rowkey或者替换rowkey的前缀部分
缺点:与resersing类似,hashing也不利于scan,因为也打乱了原rowkey的自然顺序
例:
rowkey与startkey 的关系:
如果startkey的值为1000 , 2000 , 3000
那么实际的region为:
第一个region 0-1000
第二个region 1000-2000
第三个region 2000-3000
第四个region 3000-无穷大
rowkey的设计为 随机数(1001)+id+create_time
那么这个rowkey将会落到第二个region上。 原理:会根据region的位数 去到rowkey上从左开始截取相同位数的值做比较,在哪个region的范围就落到哪个region上。
1.3列族设计
建议不超过2-3个列簇,因为当某个列簇在flush的时候,它邻近的列簇也会因关联效应被处罚flush,最终导致系统产生更多的IO
建议:将所有相关性很强的key-value都放在同一个列簇下,这样既能做到查询效率最高,也能保持尽可能少的访问不同的磁盘文件
1.4 Max Version 最大版本数
可以通过参数HColumnDescriptor.setMaxVersions(int maxVersions)
设置表中数据的最大版本。
如果应用场景只需要保存最新的数据那么可以设置为
HColumnDescriptor.setMaxVersions(1)
- 一些参数调优
2.1 hbase.client.write.buffer Hbase客户端写入缓冲区大小
2.2 hbase.regionserver.handler.count Regionserver中启动RPC服务器实例数量;该参数调小点,适用于单次请求内存消耗较高的Big PUT场景;调大点,适用于单次请求内存消耗低,远程过程调用要求高的场景。需要配合 hbase.client.write.buffer参数进行调优。
估计服务器已使用内存的数量,请用值“hbase.client.write.buffer” *“hbase.regionserver.handler.count”。
2.3 hbase.hregion.max.filesize: HStorefile最大值,超过该值则进行切分region
2.4 hbase.regionserver.global.memstore.upperLimit:regionserver中所有region的所有memstore总占堆内存比(默认为0.4)高水位线,也就是说当regionserver下的memstore的总和等于这个占比的时候进行flush操作
2.5 hbase.regionserver.global.memstore.size.lower.limit:regionserver中所有region的所有memstore总占堆内存比(默认为0.95)低水位线,也就是说在进行flush操作时regionserver下的memstore的总和等于0.4*0.95=0.38;
(long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
2.6 hfile.block.cache.size;HFile使用的缓存占分配的堆内存占比(默认值为0.2);当然是越大越好,如果写比读少很多,开到0.4-0.5也没问题。如果读写较均衡,0.3左右。如果写比读多,就默认值即可
2.7 hbase.hstore.blockingStoreFiles;store阻塞存储文件
hbase.hstore.blockingWaitTime;store阻塞等待时间;
当store中有超过blockingstorefiles的数量时,会阻塞对HRegion的更新,直到完成storefile的合并或者超过blockingWaitTime的值;所以理论上为了减少写请求的阻塞从而影响regionserver的响应时间,可以将blockingstorefiles值设置无限大。
2.8 hbase.hregion.memstore.block.multiplier;hbase memstore块乘法器;
当一个region里的memstore占用内存大小超过hbase.hregion.memstore.flush.size两倍的大小时,block该region的所有请求,进行flush,释放内存。虽然我们设置了region所占用的memstores总内存大小,比如64M,但想象一下,在最后63.9M的时候,我Put了一个200M的数据,此时memstore的大小会瞬间暴涨到超过预期的hbase.hregion.memstore.flush.size的几倍。这个参数的作用是当memstore的大小增至超过hbase.hregion.memstore.flush.size 2倍时,block所有请求,遏制风险进一步扩大;
所以需要预估应用场景中会不会出现突发写请求暴涨,如果有可以调大这个参数值
2.9 hbase.hregion.memstore.flush.size;单个region的memstore的值达到该值时会自动触发flush操作(默认值为128M)
- 分别调整Master和regionserver的java堆栈大小(heapsize)
由于默认的regionserver的内存才1G,而memstore默认是占40%,所以分配给memstore的才400M,在实际生产环境下很容易阻塞,可以通过HBASE_HEAPSIZE参数分别调整Master和RegionServer的内存