大数据系列(五)NoSQL数据库Hbase之性能优化以及容灾监控

优化策略

大部分都是理论,需要在真正场景中不停使用,总结经验。

什么会导致性能下降

  • JVM内存分配与GC回收策略
  • HBase部分配置不合理
  • 表结构、索引都设计与用户使用方式不合理
  • 小文件过多执行compact合并操作或者Region过大执行split操作,都会影响HBase的读写

回顾一下HBase写入过程,当memstore达到一定大小的时候会flush到磁盘保存成HFile,HFile过多就会执行compact操作进行合并(当store只包含一个文件的时候查询效率最优),这个时候会极大影响HBase的性能。当一个Region过大,会自动执行split,分配给多个RegionServer,也会消耗HBase性能

什么是compact?这里简单介绍一下compact分为minor compactmajor compact

  • minor compact:选取小的、相邻的storeFile进行合并,变成一个大的file
  • major compact:将所有的storeFile合并成一个file,并且清理无意义的数据(包括被删除的、TTL过期的、版本号超过设定版本号的数据)

当memstore被flush到磁盘、客户端执行compact命令、HBase后台现成周期性触发都会导致compact的执行

常见的服务端配置优化

jvm与GC设置

这里推荐一篇博客:https://blog.csdn.net/xiefu5hh/article/details/53090725

hbase-site.xml部分属性配置

我们根据实际生产环境进行配置
在这里插入图片描述
在这里插入图片描述

hbase.hregion.majorcompaction

配置major合并的间隔时间,默认为1天,可设置为0,禁止自动的major合并,可手动或者通过脚本定期进行major合并,有两种compact:minor和major,minor通常会把数个小的相邻的storeFile合并成一个大的storeFile,minor不会删除标示为删除的数据和过期的数据,major会删除需删除的数据,major合并之后,一个store只有一个storeFile文件,会对store的所有数据进行重写,有较大的性能消耗。

hbase.regionserver.handler.count

该设置决定了处理RPC的线程数量,默认值是10,通常可以调大,比如:150,当请求内容很大(上MB,比如大的put、使用缓存的scans)的时候,如果该值设置过大则会占用过多的内存,导致频繁的GC,或者出现OutOfMemory,因此该值不是越大越好。

hbase.hregion.max.filesize

配置region大小,0.94.12版本默认是10G,region的大小与集群支持的总数据量有关系,如果总数据量小,则单个region太大,不利于并行的数据处理,如果集群需支持的总数据量比较大,region太小,则会导致region的个数过多,导致region的管理等成本过高,如果一个RS配置的磁盘总量为3T*12=36T数据量,数据复制3份,则一台RS服务器可以存储10T的数据,如果每个region最大为10G,则最多1000个region,如此看,94.12的这个默认配置还是比较合适的,不过如果要自己管理split,则应该调大该值,并且在建表时规划好region数量和rowkey设计,进行region预建,做到一定时间内,每个region的数据大小在一定的数据量之下,当发现有大的region,或者需要对整个表进行region扩充时再进行split操作,一般提供在线服务的hbase集群均会弃用hbase的自动split,转而自己管理split。

file.block.cache.size

block cache的内存大小限制,默认值0.25,在偏向读的业务中,可以适当调大该值,具体配置时需试hbase集群服务的业务特征,结合memstore的内存占比进行综合考虑。

hbase.hstore.compactionThreshold

HStore的storeFile数量>= compactionThreshold配置的值,则可能会进行compact,默认值为3,可以调大,比如设置为6,在定期的major compact中进行剩下文件的合并。

hbase.hstore.blockingStoreFiles

HStore的storeFile的文件数大于配置值,则在flush memstore前先进行split或者compact,除非超过

hbase.hstore.blockingWaitTime配置的时间,默认为7,可调大,比如:100,避免memstore不及时flush,当写入量大时,触发memstore的block,从而阻塞写操作。

hbase.hregion.memstore.block.multiplier

默认值2,如果memstore的内存大小已经超过了
hbase.hregion.memstore.flush.size的2倍,则会阻塞memstore的写操作,直到降至该值以下,为避免发生阻塞,最好调大该值,比如:4,不可太大,如果太大,则会增大导致整个RS的memstore内存超过memstore.upperLimit限制的可能性,进而增大阻塞整个RS的写的几率。如果region发生了阻塞会导致大量的线程被阻塞在到该region上,从而其它region的线程数会下降,影响整体的RS服务能力

hbase.hregion.memstore.flush.size

默认值128M,单位字节,超过将被flush到hdfs,该值比较适中,一般不需要调整。

hbase.regionserver.global.memstore.upperLimit

默认值0.4,RS所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,直到总内存比例降至该数限制以下,并且在降至限制比例以下前将阻塞所有的写memstore的操作,在以写为主的集群中,可以调大该配置项,不建议太大,因为block cache和memstore cache的总大小不会超过0.8,而且不建议这两个cache的大小总和达到或者接近0.8,避免OOM,在偏向写的业务时,可配置为0.45,memstore.lowerLimit保持0.35不变,在偏向读的业务中,可调低为0.35,同时memstore.lowerLimit调低为0.3,或者再向下0.05个点,不能太低,除非只有很小的写入操作,如果是兼顾读写,则采用默认值即可。

hbase.hregion.memstore.flush.size

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

hbase.regionserver.global.memstore.lowerLimit

默认值0.35,RS的所有memstore占用内存在总内存中的lower比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,配置时需结合memstore.upperLimit和block cache的配置。
同upperLimit,只不过lowerLimit在所有region的memstores所占用内存达到Heap的[35%]时,不flush所有的memstore。它会找一个memstore内存占用最大的region,做个别flush,此时写更新还是会被block。lowerLimit算是一个在所有region强制flush导致性能降低前的补救措施。在日志中,表现为 “** Flush thread woke up with memory above low water.”

hfile.block.index.cacheonwrite

在index写入的时候允许put无根(non-root)的多级索引块到block cache里,默认是false,设置为true,或许读性能更好,但是是否有副作用还需调查。

hbase.regionserver.regionSplitLimit

控制最大的region数量,超过则不可以进行split操作,默认是Integer.MAX,可设置为1,禁止自动的split,通过人工,或者写脚本在集群空闲时执行。如果不禁止自动的split,则当region大小超过hbase.hregion.max.filesize时会触发split操作(具体的split有一定的策略,不仅仅通过该参数控制,前期的split会考虑region数据量和memstore大小),每次flush或者compact之后,regionserver都会检查是否需要Split,split会先下线老region再上线split后的region,该过程会很快,但是会存在两个问题:1、老region下线后,新region上线前client访问会失败,在重试过程中会成功但是如果是提供实时服务的系统则响应时长会增加;2、split后的compact是一个比较耗资源的动作。

常见的优化策略

预先分区

HBase在建表的时候默认只在一个RegionServer上建立一个Region,写数据的时候默认全部写到这个Region中。当此Region达到默认的大小时,会自动执行split操作进行分割(耗时、并暂时无法访问)。

  • 创建HBase表的时候,我们可以预先创建一些空的Regions,并且规定好每个Region存储的RowKey的范围,这样指定范围的数据会被写入到指定的Region中,减少了IO操作,
  • 解决HBase中数据倾斜的问题:我们可以将经常访问的数据放在不同的Region当中,不经常访问的数据放在一个Region当中)

RowKey优化

还记得数据是怎么存储的吗?rowKey1 column=family1:column1, timestamp=1557658487147, value=test for data

  • 利用HBase默认的排序特点,将一起访问的数据放在一起
  • 防止热点问题:多client集中访问同一些数据,导致集群中某些节点过载。我们的RowKey的设计应该避免时序、单调递增递减,我们可以对RowKey进行加盐、反转、哈希等方式设计。RowKey符合唯一原则并且尽可能不要过长

Column优化

  • 列蔟的名称和列的名称尽可能的短
  • 同一张表的列蔟(ColumnFamily)不要超过3个

Schema优化

HBase表在设计的过程中根据业务场景设计成高表或者宽表

  • 高表:列少杭多,一列存储数据较多,查询性能更优,吞吐量大
  • 宽表:列多行少,一行当中存储的数据量多,事务性更优

读写性能优化

写性能优化

  • 异步提交:默认是同步提交,那么在业务允许范围内,我们可以实施异步化操作
  • WAL(Write-Ahead-Log):当业务允许,部分异常数据允许丢失,而集群更加注重吞吐量,那么可以考虑关闭WAL。这里有一篇具体介绍WAL文章:https://www.jianshu.com/p/459271d78131

读性能优化

scan检索的时候,可以设置scan的cache,通常情况下,client一般情况下是分批(分页)读取数据到本地。加载到本地后,会存储在scan的缓存中(默认100条),我们可以将cache设置大一些,减少RPC请求

  • 指定列蔟检索:一个表有多个列蔟的情况下,每个列蔟可能存在多个Region中,如果我们不指定列蔟检索,可能性能较低
  • 客户端缓存:根据业务和数据量设置cache,分页分批读取数据
  • 服务端缓存:如果每次读取数据,BlockCache不能有效的命中,那么我们需要检查BlockCache的配置是否合理,Hfile是否过多(可进行compact)
  • 表结构设计:根据业务场景,可以对列蔟、列等进行优化

HBase Coprocessor(协处理器)

这里不介绍具体的使用,有兴趣的童鞋可以自行搜索

HBase受到BigTable协处理器启发,为用户提供类库和运行时环境,使代码可以在HBase RegionServer和Master上面运行(是不是很类似Lambda或者事件驱动的无服务计算,让你的代码运行在事件之上)

  • 系统协处理器:全局加载到RegionServer管理的所有表和Region上,作用于整个集群
  • 表协处理器:指定某张表使用协处理器,作用于某张表
  • Observer(观察者):类似于关系型数据库的触发器
  • Endpoint(终端):类似于关系型数据库中的存储过程

Observer应用场景

  • 安全性检查:Observer类似于触发器,那么它可以在Get或者Put等操作前,通过preGet或者prePut方法,根据业务检查是否允许操作
  • 引用完整性约束:HBase并不支持这种关系型数据库中引用完整性约束概念,但是可以通过协处理器达到目的
  • 二级索引:可以使用协处理器来维持一个二级索引

容灾与监控

备份和恢复

copyTable

  • 支持时间区间、row区间的拷贝,在拷贝过程中可以更改表名称、列簇名称,也可以拷贝已经被删除的数据
  • 使用scan进行查询数据,在拷贝数据写入时,才有put和delete这些标准的API(都是最基础的clientAPI进行读写)

首先我们进入hbase的shell,然后list查看有哪些表,这里博主只有一张表:hbase_demo, 内容如下(一行数据,一个rowKey1,一个列簇family1):

rowKey1 column=family1:column1, timestamp=1557658487147, value=test for data

我们来创建一个有一样列簇的表,用来做copyTable测试:

create ‘copyTableTest’,‘family1’

接下来我们执行copyTable操作,在hbase_hone/bin下执行

./hbase org.apache.hadoop.hbase.mapreduce.CopyTable --new.name=copyTableTest hbase_demo
发现启动了mapreduce,进行操作,最终结果显示如下:
在这里插入图片描述

我们再来看一下copyTableTest中的数据,使用命令scan ‘copyTableTest’,发现已经有数据了,数据和hbase_demo表中一样

rowKey1 column=family1:column1, timestamp=1557658487147, value=test for data

Export/Import

  • Export可导出数据到目标集群,然后在目标集群Import导入数据。
  • Export支持指定开始和结束时间,所以可以做增量备份
  • Export导出工具和CopyTable一样,依赖hbase的scan等基础操作
  • Export命令是:bin/hbase org.apache.hadoop.hbase.mapreduce.Export [TableName] hdfs://master:9000/hbase/export [version] [startTime ] [endTime], 将hbase_demo 中数据导出到hdfs的/hbase/export 中,后面version、startTime 、endTime可指定
  • Import命令是:bin/hbase -Dhbase.import.version=0.38 org.apache.hadoop.hbase.mapreduce.Import [TableName] hdfs://master:9000/hbase/export , 将hbase_demo 中数据导出到hdfs的/hbase/export 中,可以指定以某些参数的方式导入

执行Export

我们将数据备份在HDFS中,首先我们执行下面命令,进行导出:

bin/hbase org.apache.hadoop.hbase.mapreduce.Export hbase_demo /hbase/export.db

然后看到启动了MR开始执行,执行结束,我们查看一下hdfs中是否有,果然存在,:
在这里插入图片描述

执行Import

数据已经导出成功,来试试导入,首先我们清空表数据:

truncate ‘hbase_demo’

接下来将刚才导出到hdfs的数据导入到hbase_demo:

bin/hbase org.apache.hadoop.hbase.mapreduce.Import hbase_demo /hbase/export.db

结束之后我们查看表数据:

scan ‘hbase_demo’

发现数据又回来啦~~

SnapShot

  • 即快照,用于表上,通过配置hbase-xml开启
<property>
	<name>hbase.snapshot.enabled</name>
	<value>true</value>
</property>
  • 可根据快照迅速恢复表数据,但是会丢失快照之后的数据

创建快照

snapshot ‘表名称’, ‘快照名称’

克隆快照

clone_snapshot ‘快照名称’, ‘克隆名称’

列出快照

list_snapshots

删除快照

delete_snapshot ‘快照名称’

恢复数据

disable ‘表名称’
restore_snapshot ‘快照名称’

快照测试

首先,list_snapshots发现没有任何快照,我们先来创建一个快照:

snapshot ‘hbase_demo’,‘snapshot_demo’
list_snapshots之后发现已经有了快照

将hbase_demo数据直接干掉(删库跑路真刺激~~):

truncate ‘hbase_demo’

不怕,还好做了快照,直接恢复:

disable ‘hbase_demo’
restore_snapshot ‘snapshot_demo’

然后查看表:scan ‘hbase_demo’, 卧槽,报错,慌了,怎么办??!! 别怕,原来是刚刚disable 掉了,现在需要开启这个表:

enable ‘hbase_demo’
scan ‘hbase_demo’

搞定收工~~

Replication

  • 通过Replication机制可以开启hbase集群的主从模式
<property>
	<name>hbase.replication</name>
	<value>true</value>
</property>
  • Replication依赖WAL日志进行同步,如果关闭WAL,则Replication不生效
    - s

HBase监控

  • 利用hadoop生态圈的开源的专业工具
  • 自己调用hadoop和hbase的JMX的接口,获取监测数据
  • 使用Ambari:可以创建、管理、监视整个hadoop生态圈集群(包含hbase、mr、hdfs等等),有兴趣的童鞋可以去了解一下,这玩意巨牛逼~~

hadoop jmx

还记我们搭建完毕的hadoop,有一个UI界面吗,我们在浏览器输入:hadoop_namenode_ip:9870, 可以看到相关的界面,那么我们来试一试这个连接:

http://namenode:9870/jmx
在这里插入图片描述

当然了,既然是restAPI,这简直就是福利,直接可以是用java调用这些api就ok了,使用下面命令,可以带参数,比如查看namenode的信息:

http://ip:9870/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo

HBase jmx

惯例,我们回顾一下HBase的WebUI:

http://ip:16010

所以,他的jmx一样的,在后面加:

http://ip:16010/jmx
在这里插入图片描述

获取master服务的信息:

http://ip:16010/jmx?qry=Hadoop:service=HBase,name=Master,sub=Server

获取RegionServer的信息(是子节点的ip):

http://node1:16030/jmx?qry=Hadoop:service=HBase,name=RegionServer,sub=Server
在这里插入图片描述

好啦~~监控的话,肯定是现成的最好,但是大家还是要知道是怎么来的,原理是什么。这样用起来会更加得心应手。在实际生产环境中,博主直接选择阿里云hadoop一整套,而不是自己搭建(其实就是懒,想托管),So监控也不是问题啦

发布了38 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zl592886931/article/details/90217681