这里拿NoSQL主要的两个代表作品做下对比(HBase和MongoDB),两者主要的区别
MongoDB是文档型数据库,整个数据都存在磁盘中,HBase是列式数据库,存储时按column family保存到对应的hdfs文件中; MongoDB支持二级索引,而HBase本身不支持二级索引; MongoDB的update是update-in-place,也就是原地更新,而HBase的修改和添加都使用put,实际上HBase内部对已经存在的rowkey数据,只是将这一数据用不同的版本号保存下来而已,HBase默认的保存版本的数量是3; HBase根据文件的大小来控制region的分裂,在HBase内部,存储时,数据按照Row key的字典序(byte order)排序存储;MongoDB根据负载来决定shards的分裂; MongoDB和HBase都支持mapreduce,不过MongoDB的mapreduce支持不够强大,调试也较麻烦; MongoDB的读效率比写高,HBase默认适合写多读少的情况。
以我们的线上使用情况,对rowkey分布均匀的情况,HBase集群的写速度单台regionserver达到3~4w/秒,单key查询一般10ms内返回(如果使用SSD硬盘会更快)。由于我们线上每天写入70多亿条数据,当时MongoDB还没有收购WiredTiger,当时MongoDB的写入锁是个很大问题,所以我们技术选型就选的HBase。
如何提升HBase的读写性能?
代立冬:首先我们需要知道影响读写性能有什么因素:
写速度关键因素 table region分布均衡; 单台region server的region数; hbase.regionserver.handler.count; hbase.regionserver.global.memstore.upperLimit; hbase.hregion.memstore.block.multiplier; hbase.hstore.blockingStoreFiles; hbase.hregion.max.filesize;
读速度关键因素 单台Region Server上的Region数; StoreFile数; bloomfilter; in-memory flag; blockcache设置; hfile.block.cache.size;
然后具体性能优化可分为服务端和客户端,服务端和客户端我列几个代表性的(详细请参见http://blog.csdn.net/odailidong/article/details/41794403)
服务端: hbase.hregion.max.filesize:默认是10G,如果任何一个column familiy里的StoreFile超过这个值, 那么这个Region会一分为二,因为Region分裂会有短暂的Region下线时间(通常在5s以内),建议手动定时分裂,可以设置为60G; hbase.hregion.majorcompaction:hbase的Region主合并的间隔时间,默认为1天,建议设置为0,禁止自动的major主合并,major合并会把一个store下所有的storefile重写为一个storefile文件,还会把有删除标识的数据删除,业务低峰期major合并; hbase.hregion.memstore.flush.size:默认值128M,单位字节,一旦有memstore超过该值将被flush,如果regionserver的jvm内存比较充足(16G以上),可以调整为256M; hbase.hstore.compaction.max:默认值为10,一次最多合并多少个storefile,避免OOM; hbase.hstore.blockingStoreFiles:默认为7,如果任何一个store(非.META.表里的store)的storefile的文件数大于该值,则在flush memstore前先进行split或者compact,同时把该region添加到flushQueue,延时刷新,这期间会阻塞写操作直到compact完成或者超过hbase.hstore.blockingWaitTime(默认90s)配置的时间,可以设置为30,避免memstore不及时flush。
客户端 hbase.client.write.buffer:默认为2M,写缓存大小,推荐设置为5M,单位是字节,当然越大占用的内存越多,此外测试过设为10M下的入库性能,反而没有5M好; hbase.client.pause:默认是1000(1s),如果你希望低延时的读或者写,建议设为200,这个值通常用于失败重试,region寻找等; hbase.client.retries.number:默认值是10,客户端最多重试次数,可以设为11,结合上面的参数,共重试时间71s; hbase.ipc.client.tcpnodelay:默认是false,建议设为true,关闭消息缓冲; hbase.client.scanner.caching:scan缓存,默认为1,避免占用过多的client和rs的内存,一般1000以内合理,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次查询的数据条数,如果是扫描数据对下次查询没有帮助,则可以设置scan的setCacheBlocks为false,避免使用缓存; 限定扫描范围:指定列簇或者指定要查询的列,指定startRow和endRow。通过java多线程入库和查询,并控制超时时间。
HBase的rowkey设计需要注意哪些情况,有哪些好的设计方案?
代立冬:整体上说rowkey设计尽量分布均匀,避免热点。常见的均匀rowkey设计可考虑逆序,加盐值打散,如果是递增数字,还可考虑使用Long.maxValue减去当前值。
rowkey的特点:
三维有序rowkey(行主键,按ascii排序) columnkey(columnFamily+qualifier) timestamp(时间戳)
举个简单例子,比如手机号分为13,15,17,18等号段,但是13开头的手机号码特别的多,这时就可以考虑逆序下是否能满足需求。rowkey的设计应具体场景具体对待。如果怎么设计,都避免不了会产生热点的问题,也不用太担心,下面这个问题是针对热点的一些处理办法。