hbase列式存储给我们画了一个很美好的大饼,好像有了它,很多问题都可以轻易解决。但在实际的使用过程当中,你会发现没有那么简单,至少一些通用的准则要遵守,还需要根据业务的实际特点进行集群的参数调整,不是一个一蹴而就的过程。

以下主要从写入方面进行一些总结,为自己以后的使用打好基础。

1.rowkey

作为hbase的天然唯一索引,很多时候我们从查询的角度进行设计,使其更满足我们查询的需要。但查的前提是数据要已经在库里,如果是离线导入,可能还好,对于实时写入hbase表来说,如果rowkey设计的不够散列,就无法保证表持续的良好的可用性。至于在读和写之间怎么进行权衡,那就又需要根据具体的业务和约束进行权衡了。

2.预分区

如果说rowkey的散列充分表达了在不同regionserver上分布的诉求,那么根据散列规则的预分区就是实现这种诉求的必备一环。一般而言,根据集群规模,可以进行足够的预分区,比如有100个regionserver节点,那么100个region的预分区或许就是不错的选择。

但是预分区也不能解决所有问题,假设rowkey足够分散,能够均匀分布到预分区上去,也很难保证预分的region不会在分裂,自然分裂的region会演化到什么情况,就需要密切关注了。

3.手工split

如上所言,假如一个自然分裂的region在后续的膨胀过程中越来越大,以至于成为一个热点,那么就需要监控到这种情况,进而及时进行干预,进行手工拆分,这样才能均匀对Region的请求,使不至于有热点的出现。手工split的时候,务必要关闭自动balance。

4.表的压缩

说起大数据,好像存储资源便无穷无尽。实际上,数据来的往往更加凶猛。所以建表时指定压缩格式,应该也是最佳实践之一。至于是lzo还是snappy,可以根据情况选择,前者压缩比更高,对cpu的消耗也相对多一些。

5.jvm的内存参数

一般都会建议设置regionserver的jvm参数,使其不至于频繁gc。在足够的内存设置的前提下,新生代的内存和老年代的内存之比按官方推荐的3/5也是不错的选择。

6.集群的部署

往往很少人会注意到这个,对于我们现在而言,150个节点完全可以放在同一个网段内,在同一个网关下。如果节点没有那么多,但还是分布在不同的网段中,网络的延迟往往会难以察觉。当然,对于大型集群而言,一个网段无法承载,那么就需要在交换机上考虑到带宽等因素。

7.autoFlush

这个是最不应该被忘掉的一点,即关闭autoFlush,并且将缓存区根据需求进行调节,默认比如2M,可以进一步调大到6M。

以下是一些集群参数的调优,供参考:

  • HBase Server 线程唤醒步骤 (hbase.server.thread.wakefrequency):该值决定了Hbase Memstore刷新的检测频率,该值默认值为10s,在数据高峰时,每秒写入的数据达到20M左右,调整该值到5s,来帮助尽量使得MemStore的值不超过128M。
  • RegionServer 中所有 Memstore 的最大大小 (hbase.regionserver.global.memstore.upperLimit):该值是小数形式的百分比,默认为0.4,该值与Hfile缓存块大小的总和不能超过0.8,不然会造成HBase启动失败,其目的是为了在Memstore占用的内存达到Java堆栈的该百分比时强制执行刷新数据到磁盘的操作,这里我们将Memstore的百分比设置为0.5,目的是为了尽量避免强制刷新。还有一个最小大小的值(hbase.regionserver.global.memstore.lowerLimit)为0.38,表示要刷新到0.38才结束刷新,未做修改,后续可以调整。
  • HFile 块缓存大小 (hfile.block.cache.size): 该值默认值为0.4,调整为0表示Hbase的磁盘写入不使用内存缓存,测试发现调整为0后性能有一定的退化,尤其是在数据刷新操作的过程中消耗的时间有所上升,这里我们把该值调整为0.3(因为hbase.regionserver.global.memstore.upperLimit已改为了0.5)。
  • HStore 阻塞存储文件 (hbase.hstore.blockingStoreFiles):该值默认为10,如在任意 HStore 中有超过此数量的 HStoreFiles,则会阻止对此 HRegion 的更新,直到完成压缩或直到超过为 'hbase.hstore.blockingWaitTime' 指定的值。将该值改大到100,就是为了减少cycle图中的第9步。

zookeeper.session.timeout
默认值:3分钟(180000ms)
说明:RegionServer与Zookeeper间的连接超时时间。当超时时间到后,ReigonServer会 被Zookeeper从RS集群清单中移除,HMaster收到移除通知后,会对这台server负责的regions重新balance,让其他存活的 RegionServer接管.
调优
这个timeout决定了RegionServer是否能够及时的failover。设置成1分钟或更低,可以减少因等待超时而被延长的failover时间。
不过需要注意的是,对于一些Online应用,RegionServer的宕机到恢复时间本身就很短的(网络闪断,crash等故障,运维可快速介入), 如果调低timeout时间,会得不偿失。因为当ReigonServer被正式从RS集群中移除时,HMaster就开始做balance了,当故障的 RS快速恢复后,这个balance动作是毫无意义的,反而会使负载不均匀,给RS带来更多负担。

hbase.regionserver.handler.count
默认值:10
说明:RegionServer的请求处理IO线程数。
调优
这个参数的调优与内存息息相关。
较少的IO线程,适用于处理单次请求内存消耗较高的Big PUT场景(大容量单次PUT或设置了较大cache的scan,均属于Big PUT)或ReigonServer的内存比较紧张的场景。
较多的IO线程,适用于单次请求内存消耗低,TPS要求非常高的场景。
这里需要注意的是如果server的region数量很少,大量的请求都落在一个region上,因快速充满memstore触发flush导致的读写锁会影响全局TPS,不是IO线程数越高越好。
压测时,开启Enabling RPC-level logging,可以同时监控每次请求的内存消耗和GC的状况,最后通过多次压测结果来合理调节IO线程数。
这里是一个案例 Hadoop and HBase Optimization for Read Intensive Search Applications,作者在SSD的机器上设置IO线程数为100,仅供参考。

hbase.hregion.max.filesize
默认值:256M
说明:在当前ReigonServer上单个Reigon的大小,单个Region超过指定值时,这个Region会被自动split成更小的region。
调优
小region对split和compaction友好,因为拆分region或compact小region里的storefile速度很快,内存占用低。缺点是split和compaction会很频繁。
特别是数量较多的小region不停地split, compaction,会使响应时间波动很大,region数量太多不仅给管理上带来麻烦,甚至引发一些Hbase的bug。
一般512以下的都算小region。

大region,则不太适合经常split和compaction,因为做一次compact和split会产生较长时间的停顿,对应用的读写性能冲击非常大。此外,大region意味着较大的storefile,compaction时对内存也是一个挑战。
当然,大region还是有其用武之地,你只要在某个访问量低峰的时间点统一做compact和split,大region就可以发挥优势了,毕竟它能保证绝大多数时间平稳的读写性能。

既然split和compaction如此影响性能,有没有办法去掉?
compaction是无法避免的,split倒是可以从自动调整为手动。
只要通过将这个参数值调大到某个很难达到的值,比如100G,就可以间接禁用自动split(RegionServer不会对未到达100G的region做split)。
再配合RegionSplitter这个工具,在需要split时,手动split。
手动split在灵活性和稳定性上比起自动split要高很多,相反,管理成本增加不多,比较推荐online实时系统使用。

内存方面,小region在设置memstore的大小值上比较灵活,大region则过大过小都不行,过大会导致flush时app的IO wait增高,过小则因store file过多读性能降低。

hbase.regionserver.global.memstore.upperLimit/lowerLimit

默认值:0.4/0.35
upperlimit说明:hbase.hregion.memstore.flush.size 这个参数的作用是 当单个memstore达到指定值时,flush该memstore。但是,一台ReigonServer可能有成百上千个memstore,每个 memstore也许未达到flush.size,jvm的heap就不够用了。该参数就是为了限制memstores占用的总内存。
当ReigonServer内所有的memstore所占用的内存综合达到heap的40%时,HBase会强制block所有的更新并flush这些memstore以释放所有memstore占用的内存。
lowerLimit说明: 同upperLimit,只不过当全局memstore的内存达到35%时,它不会flush所有的memstore,它会找一些内存占用较大的 memstore,个别flush,当然更新还是会被block。lowerLimit算是一个在全局flush前的补救措施。可以想象一下,如果 memstore需要在一段时间内全部flush,且这段时间内无法接受写请求,对HBase集群的性能影响是很大的。
调优:这是一个Heap内存保护参数,默认值已经能适用大多数场景。它的调整一般是为了配合某些专属优化,比如读密集型应用,将读缓存开大,降低该值,腾出更多内存给其他模块使用。
这个参数会给使用者带来什么影响?
比如,10G内存,100个region,每个memstore 64M,假设每个region只有一个memstore,那么当100个memstore平均占用到50%左右时,就会达到lowerLimit的限制。 假设此时,其他memstore同样有很多的写请求进来。在那些大的region未flush完,就可能又超过了upperlimit,则所有 region都会被block,开始触发全局flush。

hfile.block.cache.size

默认值:0.2
说明:storefile的读缓存占用Heap的大小百分比,0.2表示20%。该值直接影响数据读的性能。
调优:当然是越大越好,如果读比写少,开到0.4-0.5也没问题。如果读写较均衡,0.3左右。如果写比读多,果断 默认吧。设置这个值的时候,你同时要参考 hbase.regionserver.global.memstore.upperLimit ,该值是 memstore占heap的最大百分比,两个参数一个影响读,一个影响写。如果两值加起来超过80-90%,会有OOM的风险,谨慎设置。

hbase.hstore.blockingStoreFiles

默认值:7
说明:在compaction时,如果一个Store(Coulmn Family)内有超过7个storefile需要合并,则block所有的写请求,进行flush,限制storefile数量增长过快。
调优:block请求会影响当前region的读写性能,将值设为单个region可以支撑的最大store file数量会是个不错的选择。最大storefile数量可通过region size/memstore size来计算。如果你将region size设为无限大,那么你需要预估一个region可能产生的最大storefile数。

hbase.hregion.memstore.block.multiplier

默认值:2
说明:当一个region里的memstore超过单个memstore.size两倍的大小时,block该 region的所有请求,进行flush,释放内存。虽然我们设置了memstore的总大小,比如64M,但想象一下,在最后63.9M的时候,我 Put了一个100M的数据或写请求量暴增,最后一秒钟put了1万次,此时memstore的大小会瞬间暴涨到超过预期的memstore.size。 这个参数的作用是当memstore的大小增至超过memstore.size时,block所有请求,遏制风险进一步扩大。
调优: 这个参数的默认值还是比较靠谱的。如果你预估你的正常应用场景(不包括异常)不会出现突发写或写的量可控,那么保持默认值即可。如果正常情况下,你的写量 就会经常暴增,那么你应该调大这个倍数并调整其他参数值,比如hfile.block.cache.size和 hbase.regionserver.global.memstore.upperLimit/lowerLimit,以预留更多内存,防止HBase server OOM。

不要在一张表里定义太多的Column Family

Hbase目前不能良好的处理超过2-3个CF的表。因为某个CF在flush发生时,它邻近的CF也会因关联效应被触发flush,最终导致系统产生很多IO。

批量导入

在批量导入数据到Hbase前,你可以通过预先创建region,来平衡数据的负载。详见 Table Creation: Pre-Creating Regions

Hbase客户端优化

AutoFlush

HTable的setAutoFlush设为false,可以支持客户端批量更新。即当Put填满客户端flush缓存时,才发送到服务端。
默认是true。

Scan Caching

scanner一次缓存多少数据来scan(从服务端一次抓多少数据回来scan)。
默认值是 1,一次只取一条。

Scan Attribute Selection

scan时建议指定需要的Column Family,减少通信量,否则scan默认会返回整个row的所有数据(所有Coulmn Family)。

Close ResultScanners

通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题。

Optimal Loading of Row Keys

当你scan一张表的时候,返回结果只需要row key(不需要CF, qualifier,values,timestaps)时,你可以在scan实例中添加一个filterList,并设置 MUST_PASS_ALL操作,filterList中add FirstKeyOnlyFilterKeyOnlyFilter。这样可以减少网络通信量。

Turn off WAL on Puts

当Put某些非重要数据时,你可以设置writeToWAL(false),来进一步提高写性能。writeToWAL(false)会在Put时放弃写WAL log。风险是,当RegionServer宕机时,可能你刚才Put的那些数据会丢失,且无法恢复。

启用Bloom Filter

Bloom Filter通过空间换时间,提高读操作性能。

hbase表的写入的更多相关文章

  1. hbase使用MapReduce操作4(实现将 HDFS 中的数据写入到 HBase 表中)

    实现将 HDFS 中的数据写入到 HBase 表中 Runner类 package com.yjsj.hbase_mr2; import com.yjsj.hbase_mr2.ReadFruitFro ...

  2. 把hdfs数据写入到hbase表

    功能:把hdfs上的数据写入到hbase表. hadoop的mapreduce输出要导入到hbase表,最好先输出HFile格式,再导入hbase,因为HFile是hbase的内部存储格式,所以导入效 ...

  3. sparkstreaming写入hbase表中总结

    执行spark代码插入数据到hbase表中去的时候,遇到的错误 1. 缺少hadoop-mapreduce-client-core-2.5.1.jar包 错误:java.lang.ClassNotFo ...

  4. hadoop执行hdfs文件到hbase表插入操作(xjl456852原创)

    本例中需要将hdfs上的文本文件,解析后插入到hbase的表中. 本例用到的hadoop版本2.7.2 hbase版本1.2.2 hbase的表如下: create 'ns2:user', 'info ...

  5. hbase大规模数据写入的优化历程

    业务背景:由于需要将ngix日志过滤出来的1亿+条用户行为记录存入Hbase数据库,以此根据一定的条件来提供近实时查询,比如根据用户id及一定的时间段等条件来过滤符合要求的若干行为记录,满足这一场景的 ...

  6. HBASE表设计

    1. 表的设计 1.1 Pre-Creating Regions 默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数 ...

  7. HBase学习——3.HBase表设计

    1.建表高级属性 建表过程中常用的shell命令 1.1 BLOOMFILTER 默认是 NONE 是否使用布隆过虑及使用何种方式,布隆过滤可以每列族单独启用 使用HColumnDescriptor. ...

  8. HBase BulkLoad批量写入数据实战

    1.概述 在进行数据传输中,批量加载数据到HBase集群有多种方式,比如通过HBase API进行批量写入数据.使用Sqoop工具批量导数到HBase集群.使用MapReduce批量导入等.这些方式, ...

  9. 一种HBase表数据迁移方法的优化

    1.背景调研: 目前存在的hbase数据迁移主要分如下几类: 根据上图,可以看出: 其实主要分为两种方式:(1)hadoop层:因为hbase底层是基于hdfs存储的,所以可以通过把hdfs上的数据拷 ...

随机推荐

  1. 【TOJ 3369】CD(二分)

    描述 Jack and Jill have decided to sell some of their Compact Discs, while they still have some value. ...

  2. MyBatis模糊查询的三种拼接方式

    1. sql中字符串拼接 SELECT * FROM tableName WHERE name LIKE CONCAT(CONCAT('%', #{text}), '%'); 2. 使用 ${...} ...

  3. 关于python的GIL

    转自依云在知乎上的回答,链接为https://www.zhihu.com/question/27245271/answer/462975593 侵删. python的多线程,其实不是真的多线程,它会通 ...

  4. 进一步理解 frame 和 bounds

    总结一下 iOS中 frame 和 bounds之间的区别    综述 frame和bounds都是描述一块矩形区域,但是他们是有区别的 frame:可视范围,可以理解为控件的大小,把控件当作边缘很薄 ...

  5. 【cisco下针对冗余链路故障备份的处理措施】

    对于中小型的网络中,为了流量的分担,可制定负载均衡方案,但往往带来的是链路的冗余.导致多条物理线路不能够最大的发挥其作用:冗余链路随可避免环路,但在实际的网络中还是存在一些需要完善的地方: 假设有一组 ...

  6. clear()、sync()、ignore()

    #include <iostream> using namespace std; int main() { int a; cin>>a; cout<<cin.rds ...

  7. spfa专题

    SPFA专题 1通往奥格瑞玛的道路 在艾泽拉斯,有n个城市.编号为1,2,3,...,n. 城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量. 每 ...

  8. 「日常训练」Soldier and Badges (CFR304D2B)

    题意 (Codeforces 546B) 问对一个序列最少需要增减几个1能使其彼此不同. 分析 模拟处理.需要注意的是,尽管题目中说了an<=3000,问题是,如果一群a全是3000呢(滑稽), ...

  9. 使用pyinstaller将Python打包为exe文件

    当我们完成一个Python项目或一个程序时,希望将Python的py文件打包成在Windows系统下直接可以运行的exe程序,那么pyInstaller就是一个很好的选择.pyInstaller可以将 ...

  10. 第二十三篇 logging模块(******)

    日志非常重要,而且非常常用,可以通过logging模块实现. 热身运动 import logging logging.debug("debug message") logging. ...