HBase性能调优

节点管理

节点下线

你可以在HBase的特定的节点上运行下面的脚本来停止RegionServer:

$ ./bin/hbase-daemon.sh stop regionserver

RegionServer会首先关闭所有的region然后把它自己关闭,在停止的过程中,RegionServer的会向Zookeeper报告说他已经过期了。master会发现RegionServer已经死了,会把它当作崩溃的server来处理。他会将region分配到其他的节点上去。

在下线节点之前要停止Load Balancer

如果在运行load balancer的时候,一个节点要关闭, 则Load Balancer和Master的recovery可能会争夺这个要下线的Regionserver。为了避免这个问题,先将load balancer停止,参见下面的 Load Balancer.

RegionServer下线有一个缺点就是其中的Region会有好一会离线。Regions是被按顺序关闭的。如果一个server上有很多region,从第一个region会被下线,到最后一个region被关闭,并且Master确认他已经死了,该region才可以上线,整个过程要花很长时间。在HBase 0.90.2中,我们加入了一个功能,可以让节点逐渐的摆脱他的负载,最后关闭。HBase 0.90.2加入了 graceful_stop.sh脚本,可以这样用,

$ ./bin/graceful_stop.sh 
Usage: graceful_stop.sh [--config &conf-dir>] [--restart] [--reload] [--thrift] [--rest] &hostname>
 thrift      If we should stop/start thrift before/after the hbase stop/start
 rest        If we should stop/start rest before/after the hbase stop/start
 restart     If we should restart after graceful stop
 reload      Move offloaded regions back on to the stopped server
 debug       Move offloaded regions back on to the stopped server
 hostname    Hostname of server we are to stop

要下线一台RegionServer可以这样做

$ ./bin/graceful_stop.sh HOSTNAME

这里的HOSTNAME是你想要下线的RegionServer的host.

关于HOSTNAME 传递到graceful_stop.sh的HOSTNAME必须和hbase使用的hostname一致,hbase用它来区分RegionServers。可以用master的UI来检查RegionServers的id。通常是hostname,也可能是FQDN。不管HBase使用的哪一个,你可以将它传到 graceful_stop.sh脚本中去,目前他还不支持使用IP地址来推断hostname。所以使用IP就会发现server不在运行,也没有办法下线了。

graceful_stop.sh 脚本会一个一个将region从RegionServer中移除出去,以减少改RegionServer的负载。他会先移除一个region,然后再将这个region安置到一个新的地方,再移除下一个,直到全部移除。最后graceful_stop.sh脚本会让RegionServer stop.,Master会注意到RegionServer已经下线了,这个时候所有的region已经重新部署好。RegionServer就可以干干净净的结束,没有WAL日志需要分割。

Load Balancer 当执行graceful_stop脚本的时候,要将Region Load Balancer关掉(否则balancer和下线脚本会在region部署的问题上存在冲突):

hbase(main):001:0> balance_switch false
true
0 row(s) in 0.3590 seconds

上面是将balancer关掉,要想开启:

hbase(main):001:0> balance_switch true
false
0 row(s) in 0.3590 seconds

14.3.2. 依次重启 你还可以让这个脚本重启一个RegionServer,不改变上面的Region的位置。要想保留数据的位置,你可以依次重启(Rolling Restart),就像这样:

$ for i in `cat conf/regionservers|sort`; do ./bin/graceful_stop.sh --restart --reload --debug $i; done &> /tmp/log.txt &

Tail /tmp/log.txt来看脚本的运行过程.上面的脚本只对RegionServer进行操作。要确认load balancer已经关掉。还需要在之前更新master。下面是一段依次重启的伪脚本,你可以借鉴它:

确认你的版本,保证配置已经rsync到整个集群中。如果版本是0.90.2,需要打上HBASE-3744 和 HBASE-3756两个补丁。

运行hbck确保你的集群是一致的

$ ./bin/hbase hbck

当发现不一致的时候,可以修复参考http://my.oschina.net/drl/blog/683885

重启Master:

$ ./bin/hbase-daemon.sh stop master; ./bin/hbase-daemon.sh start master

关闭region balancer:

$ echo "balance_switch false" | ./bin/hbase

在每个RegionServer上运行graceful_stop.sh:

$ for i in `cat conf/regionservers|sort`; do ./bin/graceful_stop.sh --restart --reload --debug $i; done &> /tmp/log.txt &

如果你在RegionServer还开起来thrift和rest server。还需要加上--thrift or --rest 选项 (参见 graceful_stop.sh 脚本的用法).

再次重启Master.这会把已经死亡的server列表清空,重新开启balancer.

运行 hbck 保证集群是一致的

配置优化

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根据故障机器记录的WAL日志进行恢复)。当故障的RS在人工介入恢复后,这个balance动作是毫无意义的,反而会使负载不均匀,给RS带来更多负担。特别是那些固定分配regions的场景。

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,既能顺利完成split和compaction,又能保证绝大多数时间平稳的读写性能。

既然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 这个参数的作用是当单个Region内所有的memstore大小总和超过指定值时,flush该region的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模式来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。 这个参数的作用是防止内存占用过大,当ReigonServer内所有region的memstores所占用内存总和达到heap的40%时,HBase会强制block所有的更新并flush这些region以释放所有memstore占用的内存。

lowerLimit说明: 同upperLimit,只不过lowerLimit在所有region的memstores所占用内存达到Heap的35%时,不flush所有的memstore。它会找一个memstore内存占用最大的region,做个别flush,此时写更新还是会被block。lowerLimit算是一个在所有region强制flush导致性能降低前的补救措施。在日志中,表现为 “** Flush thread woke up with memory above low water.”

调优:这是一个Heap内存保护参数,默认值已经能适用大多数场景。 参数调整会影响读写,如果写的压力大导致经常超过这个阀值,则调小读缓存hfile.block.cache.size增大该阀值,或者Heap余量较多时,不修改读缓存大小。 如果在高压情况下,也没超过这个阀值,那么建议你适当调小这个阀值再做压测,确保触发次数不要太多,然后还有较多Heap余量的时候,调大hfile.block.cache.size提高读性能。 还有一种可能性是?hbase.hregion.memstore.flush.size保持不变,但RS维护了过多的region,要知道 region数量直接影响占用内存的大小。

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

说明:在flush时,当一个region中的Store(Coulmn Family)内有超过7个storefile时,则block所有的写请求进行compaction,以减少storefile数量。 调优:block写请求会严重影响当前regionServer的响应时间,但过多的storefile也会影响读性能。从实际应用来看,为了获取较平滑的响应时间,可将值设为无限大。如果能容忍响应时间出现较大的波峰波谷,那么默认或根据自身场景调整即可。

hbase.hregion.memstore.block.multiplier

默认值:2

说明:当一个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所有请求,遏制风险进一步扩大。

调优: 这个参数的默认值还是比较靠谱的。如果你预估你的正常应用场景(不包括异常)不会出现突发写或写的量可控,那么保持默认值即可。如果正常情况下,你的写请求量就会经常暴长到正常的几倍,那么你应该调大这个倍数并调整其他参数值,比如hfile.block.cache.size和hbase.regionserver.global.memstore.upperLimit/lowerLimit,以预留更多内存,防止HBase server OOM。

hbase.hregion.memstore.mslab.enabled

默认值:true

说明:减少因内存碎片导致的Full GC,提高整体性能。

调优:详见 http://kenwublog.com/avoid-full-gc-in-hbase-using-arena-allocation

其他

启用LZO压缩

LZO对比Hbase默认的GZip,前者性能较高,后者压缩比较高,具体参见?Using LZO Compression 。对于想提高HBase读写性能的开发者,采用LZO是比较好的选择。对于非常在乎存储空间的开发者,则建议保持默认。

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

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

批量导入

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

避免CMS concurrent mode failure

HBase使用CMS GC。默认触发GC的时机是当年老代内存达到90%的时候,这个百分比由 -XX:CMSInitiatingOccupancyFraction=N 这个参数来设置。concurrent mode failed发生在这样一个场景: 当年老代内存达到90%的时候,CMS开始进行并发垃圾收集,于此同时,新生代还在迅速不断地晋升对象到年老代。当年老代CMS还未完成并发标记时,年老代满了,悲剧就发生了。CMS因为没内存可用不得不暂停mark,并触发一次stop the world(挂起所有jvm线程),然后采用单线程拷贝方式清理所有垃圾对象。这个过程会非常漫长。为了避免出现concurrent mode failed,建议让GC在未到90%时,就触发。

通过设置-XX:CMSInitiatingOccupancyFraction=N

这个百分比, 可以简单的这么计算。如果你的hfile.block.cache.size 和hbase.regionserver.global.memstore.upperLimit 加起来有60%(默认),那么你可以设置 70-80,一般高10%左右差不多。

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可能会出现问题(对应的Server资源无法释放)。

Optimal Loading of Row Keys

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

Turn off WAL on Puts

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

启用Bloom Filter

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

major_compact 'testtable'

通常生产环境会关闭自动major_compact(配置文件中hbase.hregion.majorcompaction设 为0),选择一个晚上用户少的时间窗口手工major_compact,如果hbase更新不是太频繁,可以一个星期对所有表做一次 major_compact,这个可以在做完一次major_compact后,观看所有的storefile数量,如果storefile数量增加到 major_compact后的storefile的近二倍时,可以对所有表做一次major_compact,时间比较长,操作尽量避免高锋期。

hbase的迁移

1,copytable方式

bin/hbase org.apache.hadoop.hbase.mapreduce.CopyTable --peer.adr=zookeeper1,zookeeper2,zookeeper3:/hbase 'testtable'

目前0.92之前的版本的不支持多版本的复制,0.94已经支持多个版本的复制。当然这个操作需要添加hbase目录里的conf/mapred-site.xml,可以复制hadoop的过来。 2,Export/Import

bin/hbase org.apache.hadoop.hbase.mapreduce.Export testtable /user/testtable [versions] [starttime] [stoptime]
    bin/hbase org.apache.hadoop.hbase.mapreduce.Import testtable  /user/testtable

跨版本的迁移,我觉得是一个不错的选择,而且copytable不支持多版本,而export支持多版本,比copytable更实用一些。 3,直接拷贝hdfs对应的文件

首先拷贝hdfs文件,如bin/hadoop distcp hdfs://srcnamenode:9000/hbase/testtable/ hdfs://distnamenode:9000/hbase/testtable/
然后在目的hbase上执行bin/hbase org.jruby.Main bin/add_table.rb /hbase/testtable

生成meta信息后,重启hbase 这个操作是简单的方式,操作之前可以关闭hbase的写入,执行flush所有表(上面有介绍),再distcp拷贝,如果hadoop版本不一致,可以用hftp接口的方式,我推荐使用这种方式,成本低

Jvm调整:

  • a、内存大小: master默认为1G,可增加到2G,regionserver默认1G,可调大到10G,或者更大,zk并不耗资源,可以不用调整,需要注意的是,调整了rs的内存大小后,需调整hbase.regionserver.maxlogs和hbase.regionserver.hlog.blocksize这两个参数,WAL的最大值由hbase.regionserver.maxlogs * hbase.regionserver.hlog.blocksize决定(默认32*32M~=1G),一旦达到这个值,就会被触发flush memstore,如果memstore的内存增大了,但是没有调整这两个参数,实际上对大量小文件没有任何改进,调整策略:hbase.regionserver.hlog.blocksize * hbase.regionserver.maxlogs 设置为略大于hbase.regionserver.global.memstore.lowerLimit * HBASE_HEAPSIZE。
  • b、垃圾回收: export HBASE_OPTS="$HBASE_OPTS -Xms30720m -Xmx30720m -XX:NewSize=512m -XX:MaxPermSize=128m -XX:PermSize=128m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/local/hbase-0.94.0/logs/gc-hbase.log"。

其它调优

1)列族、rowkey要尽量短,每个cell值均会存储一次列族名称和rowkey,甚至列名称也要尽量短

2)RS的region数量:一般每个RegionServer不要过1000,过多的region会导致产生较多的小文件,从而导致更多的compact,当有大量的超过5G的region并且RS总region数达到1000时,应该考虑扩容。

3)建表时: a、如果不需要多版本,则应设置version=1; b、 开启lzo或者snappy压缩,压缩会消耗一定的CPU,但是,磁盘IO和网络IO将获得极大的改善,大致可以压缩4~5倍; c、合理的设计rowkey,在设计rowkey时需充分的理解现有业务并合理预见未来业务,不合理的rowkey设计将导致极差的hbase操作性能; d、合理的规划数据量,进行预分区,避免在表使用过程中的不断split,并把数据的读写分散到不同的RS,充分的发挥集群的作用; e、列族名称尽量短,比如:“f”,并且尽量只有一个列族; f、视场景开启bloomfilter,优化读性能。

Client端调优

1、hbase.client.write.buffer:写缓存大小,默认为2M,推荐设置为6M,单位是字节,当然不是越大越好,如果太大,则占用的内存太多;

2、hbase.client.scanner.caching:scan缓存,默认为1,太小,可根据具体的业务特征进行配置,原则上不可太大,避免占用过多的client和rs的内存,一般最大几百,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次查询的数据条数,比如:业务特点决定了一次最多50条,则可以设置为50

3、设置合理的超时时间和重试次数,具体的内容会在后续的blog中详细讲解。

4、client应用读写分离 读和写分离,位于不同的tomcat实例,数据先写入redis队列,再异步写入hbase,如果写失败再回存redis队列,先读redis缓存的数据(如果有缓存,需要注意这里的redis缓存不是redis队列),如果没有读到再读hbase。 当hbase集群不可用,或者某个RS不可用时,因为HBase的重试次数和超时时间均比较大(为保证正常的业务访问,不可能调整到比较小的值,如果一个RS挂了,一次读或者写,经过若干重试和超时可能会持续几十秒,或者几分钟),所以一次操作可能会持续很长时间,导致tomcat线程被一个请求长时间占用,tomcat的线程数有限,会被快速占完,导致没有空余线程做其它操作,读写分离后,写由于采用先写redis队列,再异步写hbase,因此不会出现tomcat线程被占满的问题, 应用还可以提供写服务,如果是充值等业务,则不会损失收入,并且读服务出现tomcat线程被占满的时间也会变长一些,如果运维介入及时,则读服务影响也比较有限。

5、如果把org.apache.hadoop.hbase.client.HBaseAdmin配置为spring的bean,则需配置为懒加载,避免在启动时链接hbase的Master失败导致启动失败,从而无法进行一些降级操作。

6、Scan查询编程优化:

1)调整caching;
2)如果是类似全表扫描这种查询,或者定期的任务,则可以设置scan的setCacheBlocks为false,避免无用缓存;
3)关闭scanner,避免浪费客户端和服务器的内存;
4)限定扫描范围:指定列簇或者指定要查询的列;
5)、如果只查询rowkey时,则使用KeyOnlyFilter可大量减少网络消耗;

7、对响应时间有严格要求的在线实时系统,可以通过封装hbase client api,通过线程池执行get等操作,通过Future.get()设置超时时间,超过时间没有返回则执行其它逻辑,实现快速失败的效果。

作为hbase依赖的状态协调者ZK和数据的存储则HDFS,也需要调优:

ZK调优:

1、zookeeper.session.timeout:默认值3分钟,不可配置太短,避免session超时,hbase停止服务,线上生产环境由于配置为1分钟,出现过2次该原因导致的hbase停止服务,也不可配置太长,如果太长,当rs挂掉,zk不能快速知道,从而导致master不能及时对region进行迁移。

2、zookeeper数量:至少5个节点。给每个zookeeper 1G左右的内存,最好有独立的磁盘。 (独立磁盘可以确保zookeeper不受影响).如果集群负载很重,不要把Zookeeper和RegionServer运行在同一台机器上面。就像DataNodes 和 TaskTrackers一样,只有超过半数的zk存在才会提供服务,比如:共5台,则最多只运行挂2台,配置4台与3台一样,最多只运行挂1台。

3、hbase.zookeeper.property.maxClientCnxns:zk的最大连接数,默认为300,应根据集群规模进行调整。

hdf调优:

  1. dfs.name.dir: namenode的数据存放地址,可以配置多个,位于不同的磁盘并配置一个NFS远程文件系统,这样nn的数据可以有多个备份
  2. dfs.data.dir:dn数据存放地址,每个磁盘配置一个路径,这样可以大大提高并行读写的能力
  3. dfs.namenode.handler.count:nn节点RPC的处理线程数,默认为10,需提高,比如:60
  4. dfs.datanode.handler.count:dn节点RPC的处理线程数,默认为3,需提高,比如:20
  5. dfs.datanode.max.xcievers:dn同时处理文件的上限,默认为256,需提高,比如:8192
  6. dfs.block.size:dn数据块的大小,默认为64M,如果存储的文件均是比较大的文件则可以考虑调大,比如,在使用hbase时,可以设置为128M,注意单位是字节
  7. dfs.balance.bandwidthPerSec:在通过start-balancer.sh做负载均衡时控制传输文件的速度,默认为1M/s,可配置为几十M/s,比如:20M/s
  8. dfs.datanode.du.reserved:每块磁盘保留的空余空间,应预留一些给非hdfs文件使用,默认值为0
  9. dfs.datanode.failed.volumes.tolerated:在启动时会导致dn挂掉的坏磁盘数量,默认为0,即有一个磁盘坏了,就挂掉dn,可以不调整。

[其它] 设置操作系统的swappiness为0,则在物理内存不够的情况下才会使用交换分区,避免GC回收时会花费更多的时间,当超过zk的session超时时间则会出现rs宕机的误报,关于swappiness具体参考:http://en.wikipedia.org/wiki/Swappiness

参考: http://wangneng-168.iteye.com/blog/2067741 http://eclecl1314-163-com.iteye.com/blog/1704249 http://www.cnblogs.com/ggjucheng/p/3381882.html http://abloz.com/hbase/book.html#arch.overview

转载于:https://my.oschina.net/drl/blog/683986

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值