HBase优化

Posted by JustDoDT on July 13, 2019

预先分区

默认情况下,在创建 HBase 表的时候会自动创建一个 Region 分区,当导入数据的时候,所有的 HBase 客户端都向这一个 Region 写数据,直到这个 Region 足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的 Region,这样当数据写入 HBase 时,会按照 Region 分区情况,在集群内做数据的负载均衡。

Rowkey 优化

HBase 中的 Rowkey 是按照字典顺序存储的,因此,设计 Rowkey 时候,要充分利用排序的特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。

此外,Rowkey 若是递增的生成,建议不要使用正序直接写入 Rowkey ,而是采用 reverse 的方式反转 Rowkey,使得 Rowkey 大致均衡分布,这样设计有个好处是能将 RegionServer 的负载均衡,否则容易产生所有新数据都在一个 RegionServer 上堆积的现象,这一点还可以结合 table 的预切分一起设计。

减少ColumnFamily 数量

不要在一张表里定义太多的 ColumnFamily。目前 HBase 并不能很好的处理超过2~~3个 ColumnFamily 的表。因为某个 CloumnFamily 在 flush 的时候,它邻近的 ColumnFamily 也会因关联效应被触发 flush ,最终导致系统产生更多的 I/O。

缓存策略

创建表的时候,可以通过 HColumnDescriptor.setlnMemory(true) 将表放到 RegionServer 的缓存中,保证在读取的时候被 cache 命中。

设置存储生命期

创建表的时候,可以通过 HColumnDescriptor.setTimeToLive(int time ToLive) 设置表中数据的存储生命期,过期数据将自动被删除。

硬盘配置

每台 RegionServer 管理10~1000 个 Regions ,每个 Region 在1~2GB,则每台 Server 至少需要10GB磁盘,最大需要1000*2GB=2TB,考虑要3备份,则要 6TB。方案一是用3块 2TB的硬盘,方案二是用12块500GB的硬盘,带宽足够时,后者能提供更大的吞吐率,更细粒度的冗余备份,更快速的单盘故障恢复。

分配合适的内存给RegionServer服务

在不影响其他服务的情况下,RS内存越大越好。例如在HBase 的 conf 目录下的 hbase-env.sh 的最后添加 export HBASE_REGIONSERVER_OPTS=”-Xmx16000m$HBASE_REGLONSERVER_OPTS”,其中16000m 为分配给 RegionServer 的内存大小。

写数据的备份数

备份数与读性能成正比,与写性能成反比,且备份数影响高可用性。有两种配置方式,一种是将 hdfs-site.xml 拷贝到 hbase 的 conf 目录下,然后在其中添加或修改配置项 dfs.replication 的值为要设置的备份数,这种修改对所有的 HBase 用户表生效,另外一种方式,是改写 HBase 代码,让 HBase 支持针对列族设置备份数,在创建表时,设置列族备份数,默认为3,此种备份数只对设置的列族生效。

WAL(预写日志)

可以设置开关,表示 HBase 在写数据前用不用先写日志,默认是打开的,关掉会提高性能,但是如果系统出现故障(负责插入的 RegionServer挂掉),数据可能会丢失。配置WAL在调用 JavaAPI 写入时候,设置 Put 实例的WAL,调用 Put.setWriteToWAL(boolean)。

批量写

HBase 的 Put 支持单条插入,也支持批量插入,一般来说批量写更快,节省来回的网络开销。在客户端调用 JavaAPI时候,先将批量的 Put 放入一个 Put 列表,然后调用 HTable 的 Put(Put列表)函数来批量写。

客户端一次从服务器拉取的数量

通过配置一次拉取的较大的数据量可以减少客户端获取数据的时间,但是它会占用客户端内存。有三个地方可以进行配置:

  • 在 HBase 的 conf 配置文件中进行配置 hbase.client.scaner.caching
  • 通过调用 HTable.setScannerCaching(intscannerCaching)进行配置
  • 通过调用 Scan.setCaching(intcaching)进行配置。三者的优先级越来越高

RegionServer 的请求处理I/O线程数

较少的IO线程适用于处理单次请求内存消耗较高的 Big Put 场景(大容量单次 Put 或设置了较大的 cache 的 Scan,均属于 Big Put)或 RegionServer 的内存比较紧张的场景。

较多的 IO 线程,适用于单次请求内存消耗低,TPS要求(美妙事务处理量(TransactionPerSecond)) 非常高的场景。设置该值的时候,以监控内存为主要参考。

在 hbase-site.xml 配置文件中配置项为 hbase.regionserver.habdler.count。

Region的大小设置

配置项为 hbase.hregion.max.filesize,所属配置文件为 hbase-site.xml.,默认大小 256M。

在当前 ReigonServer 上单个 Reigon 的最大存储空间,单个 Region 超过该值时,这个 Region 会被自动 split成更小的 Region。小 Region 对 split 和 compaction 友好,因为拆分 Region 或 compact 小 Region 里的StoreFile 速度很快,内存占用低。缺点是 split 和 compaction 会很频繁,特别是数量较多的小 Region 不停地split, compaction,会导致集群响应时间波动很大,Region 数量太多不仅给管理上带来麻烦,甚至会引发一些Hbase 的 bug。一般 512M 以下的都算小 Region。大 Region 则不太适合经常 split 和 compaction,因为做一次 compact 和 split 会产生较长时间的停顿,对应用的读写性能冲击非常大。

此外,大 Region 意味着较大的 StoreFile,compaction 时对内存也是一个挑战。如果你的应用场景中,某个时间点的访问量较低,那么在此时做 compact 和 split,既能顺利完成 split 和 compaction,又能保证绝大多数时间平稳的读写性能。compaction 是无法避免的,split 可以从自动调整为手动。只要通过将这个参数值调大到某个很难达到的值,比如 100G,就可以间接禁用自动 split(RegionServer 不会对未到达 100G 的 Region 做split)。再配合 RegionSplitter 这个工具,在需要 split 时,手动 split。手动 split 在灵活性和稳定性上比起自动split 要高很多,而且管理成本增加不多,比较推荐 online 实时系统使用。内存方面,小 Region 在设置memstore 的大小值上比较灵活,大 Region 则过大过小都不行,过大会导致 flush 时 app 的 IO wait 增高,过小则因 StoreFile 过多影响读性能。

操作系统参数

Linux 系统最大可以打开的文件数一般默认的参数是1024,如果你不进行修改并发量上来的时候就会出现“Too Many Open Files”的错误,导致整个HBase不可运行,你可以用ulimit -n 命令进行修改,或者修改/etc/security/limits.conf和/proc/sys/fs/file-max 的参数,具体如何修改可以去Google 关键字 “linux limits.conf ”

JVM 配置

修改 hbase-env.sh 文件中的配置参数,根据你的机器硬件和当前操作系统的JVM(32/64位)配置适当的参数

HBASE_HEAPSIZE 4000 HBase使用的 JVM 堆的大小

HBASE_OPTS “‐server ‐XX:+UseConcMarkSweepGC”JVM GC 选项

HBASE_MANAGES_ZKfalse 是否使用Zookeeper进行分布式管理

持久化

重启操作系统后HBase 中数据全无,你可以不做任何修改的情况下,创建一张表,写一条数据进行,然后将机器重启,重启后你再进入HBase的shell 中使用 list 命令查看当前所存在的表,一个都没有了。这是为啥?其实就是没有对数据进行持久化到磁盘造成的。因此你可以在 $HBASE_HOME/conf/hbase-default.xml中设置hbase.rootdir的值,来设置文件的保存位置指定一个文件夹,例如:file:///you/hbase-data/path,你建立的HBase中的表和数据就直接写到了你的磁盘上,同样你也可以指定你的分布式文件系统HDFS的路径例如:hdfs://NAMENODE_SERVER:PORT/HBASE_ROOTDIR,这样就写到了你的分布式文件系统上了。

缓冲区大小

hbase.client.write.buffer

这个参数可以设置写入数据缓冲区的大小,当客户端和服务端传输数据,服务器为了提高系统运行性能开辟一个写的缓冲区来处理它,这个参数设置如果设置大了,将会对系统的内存有一定的要求,直接影响系统的性能。

扫描目录数

hbase.master.meta.thread.rescanfrequency

定义多长时间HMaster 对系统表 root 和 meta 扫描一次,这个参数可以设置得长一些,降低系统的能耗。

split/compaction时间间隔

hbase.regionserver.thread.splitcompactcheckfrequency

这个参数是表示多久去 RegionServer 服务器运行一次 split/compaction 的时间间隔,当然split之前会先进行一个compact操作.这个compact操作可能是minorcompact也可能是major compact.compact后,会从所有的Store下的所有StoreFile文件最大的那个取midkey.这个midkey可能并不处于全部数据的mid中 一个row-key的下面的数据可能会跨不同的HRegion。

缓存在 JVM 堆中分配的百分比

hfile.block.cache.size

指定HFile/StoreFile 缓存在JVM堆中分配的百分比,默认值是0.2,意思就是20%,而如果你设置成0,就表示对该选项屏蔽。

Zookeeper 客户端同时访问的并发连接数

hbase.zookeeper.property.maxClientCnxns

这项配置的选项就是从zookeeper中来的,表示ZooKeeper客户端同时访问的并发连接数,ZooKeeper对于HBase来说就是一个入口这个参数的值可以适当放大些。

memstore 占用堆的大小参数配置

hbase.regionserver.global.memstore.upperLimit

在RegionServer中所有memstores占用堆的大小参数配置,默认值是0.4,表示40%,如果设置为0,就是对选项进行屏蔽。

Memstore 中缓存写入大小

hbase.hregion.memstore.flush.size

Memstore中缓存的内容超过配置的范围后将会写到磁盘上,例如:删除操作是先写入MemStore里做个标记,指示那个value, column 或 family等下是要删除的,HBase会定期对存储文件做一个major compaction,在那时HBase会把MemStore刷入一个新的HFile存储文件中。如果在一定时间范围内没有做major compaction,而Memstore中超出的范围就写入磁盘上了。