1. 垃圾回收优化
用户可以通过向hbase-env.sh文件中添加HBASE_OPTS或者HBASE_REGIONSERVER_OPT来设置垃圾回收相关选项,后者仅仅影响region服务器进程,也是推荐的修改方式。
增加新生代大小, 减小新生代垃圾回收次数
-XX:MaxNewSize=8g -XX:NewSize=8g
修改垃圾回收策略
-XX:+UseParNewGC
设置年轻代使用Parallel New Collector策略,这将停止运行Java进程而去清空年轻代堆。与老年代相比年轻代很小,所以这个过程话费时间很短,通常几百毫秒。
-XX:+UseConcMarkSweepGC
以上策略如果用于年老代会造成region server几秒钟甚至数分钟停顿,如果停顿时间超过zookeeper会话超时限制,这个服务器会被master认为已经崩溃,并且随后会被抛弃。
这种情况可以使用并行标记回收策略(Concurrent Mark-Sweep Collector, CMS)来缓解, 不同之处在于其工作时试图在不停止运行Java进程的情况下异步并行的完成工作。这种策略将增加CPU的负担,但是却可以避免重写老生代碎片时的停顿--除非发生提示失败,这种失败会迫使垃圾回收暂停运行Java进程并进行内存整理。
2. 本地memstore分配缓冲区
由于memstore不断创建和释放内存空间, 就会在年老代Heap上产生孔洞。申请新空间时,由于碎片过多导致没有足够大的连续空间分配,JRE会退回到使用(stop the world)垃圾回收器,这样会导致其重写整个堆空间并压缩剩余的可用对象。
MSLAB(memstore-local allocation buffers)是许多固定大小的缓冲区,用来存储大小不同的keyvalue实例。当一个缓冲区不能放下一个新加入的keyvalue时,系统就认为这个缓冲区已经占满了,然后创建一个新的固定大小的缓冲区。 一旦这些缓冲区对象被回收,他们将在堆中留下固定大小的孔洞,之后调用固定大小的新对象将会重新使用这些孔洞,这样就不需要JRE停止压缩回收堆内存了。
但是mslab也有一些副作用,比如更加浪费堆空间;使用缓冲区需要额外的内存复制工作,比直接使用keyvalue实例要稍微慢一点
配置hbase-site.xml中的 hbase.hregion.memstore.mslab.enabled 默认值 true
3 压缩
除非存储已经压缩过的内容如JPEG图像,对于其它场景来说,压缩通常会带来较好的性能,因为CPU压缩和解压的时间比从磁盘读取和写入更多数据消耗的时间更短。
算法 | 压缩比 % | 压缩 MB/S | 解压 MB/S |
GZIP | 13.4 | 21 | 118 |
LZO | 20.5 | 135 | 410 |
Zippy/Snappy | 22.2 | 172 | 409 |
默认Hbase对文件是没有压缩的, 查看 describe 'tablename'
4. 优化拆分和合并
通常Hbase是自动处理Region拆分的,一旦它们到了预定的阈值,region将被拆分成两个,之后它们可以接受新的数据并继续增长。 当用户的region大小已恒定速度增长时,region拆分会在同一时间发生,因为同时需要压缩region中的存储文件,这个过程会重写拆分之后的数据,这将引起IO上升,称之为“拆分和并风暴”。
与其依赖自动拆分,不如关闭这个行为调用split和major_compace命令手动拆分。
为防止自动拆分可设置hbase.hregion.max.filesize的值为一个比较大的值,比如100GB。 然后用客户端实现一个调用split()和 majorCompact()的客户端,也可以使用shell交互的调用相关命令,或者使用cron定时的调用它们。
另一种方法是创建表时进行预拆分
create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
5.负载均衡
master内置了一个叫做均衡器的特性,默认情况下每5分钟(通过hbase.balancer.period设置)运行一次。 一旦均衡器启动,它会尝试均匀分配region到所有region服务器。用户可通过shell的balance_switch命令来更改均衡器的开启或关闭状态。
除了依赖均衡器自动完成工作,用户还可以使用move命令显示地将region移动到另一个region server上。
6. 合并region
当用户删除大量数据并且想减少每个服务器管理的region数量,可以使用merge_region命令合并相邻的region。
7 客户端API最佳实践
禁止自动刷新
put.setAutoFlush(false)
使用扫描缓存
scan.setCaching(1000);
限定扫描范围
尽量只在一个Family中扫描
关闭ResultScanner
一定要在try catch 的 finally 中关闭ResultScanner
快缓存用法
scan.setCacheBlocks() 对于那些频繁访问的行,建议使用块缓存
优化获取行健的方式当用户仅需要获取需要的行健时,在Scan中用setFilter()方法添加一个带MUST_PASS_ALL的FilterList。FilterList中包含FirstKeyFilter和KeyOnlyFilter两个过滤器。使用以上的组合过滤器将会把发现的第一个keyvalue行健返回给客户端。
关闭put上的WAL
当需要存入的数据对准确度要求不是很高时,使用Put的writeToWAL(false)来关闭WAL。