作者简介 张洋洋 百度高级研发工程师
负责百度智能运维产品(Noah)的分布式时序数据库和通用配额管理平台的设计研发工作,在分布式存储和配额管理方向有广泛的实践经验。
干货概览
通过百度大规模时序数据存储系列文章的介绍,想必读者对百度智能监控系统Noah的TSDB不再陌生,它主要用来存储Noah监控系统的时序指标数据,包括但不限于硬件和软件的可用性指标、资源使用率指标和性能指标等。如《百度大规模时序数据存储(二)|存储选型及数据模型设计》文章所述, Noah-TSDB是基于HBase为底层存储的基础上自主研发的,其优秀的性能离不开HBase的贡献。今天主要聊聊在百度智能监控场景下的HBase相关实践经验,先简单介绍一下HBase。
HBase架构简介
HBase是一个基于Java、开源的、非关系型的、面向列存储的分布式可扩展的大数据存储数据库。HBase的集群主要由HMater和RegionServer两种角色组成,底层以HDFS作为存储设施,集群由Zookeeper协助管理。其架构如下图所示:
图1 HBase架构图
简单介绍一下HBase中相关组件的作用:
HMaster
HMaster 是整个集群的大脑,负责数据表的操作、集群的负载均衡和故障恢复等集群管理工作。
RegionServer
HBase 将表以行为单位划分成许多片段,每个片段称为一个 Region。这些Region被分配到RegionServer进行管理。在读写流程中,定位到数据所在RegionServer后,Client与RegionServer直接交互进行数据的读写。
Zookeeper
HBase作为一个大规模的分布式系统,Zookeeper的作用是至关重要的。首先Zookeeper作为HMaster HA解决方案,保证了至少有一个HMaster处于工作状态。其次Zookeeper通过心跳机制探活RegionServer,当RegionServer故障时及时通知HMaster进行故障处理工作。最后Zookeeper保存了维护全局元信息的META表的路径,Client第一次与HBase集群交互时,需要通过META表来获取目标数据所在的RegionServer。
上面简单介绍了HBase的架构和各组件的基本信息,下面和大家分享一下在百度最大规模时序数据库的场景下使用HBase时遇到的几个典型问题和优化方案。
热点问题
大家都知道木桶效应,对于TSDB系统来说,热点Region所在的RegionServer就是影响整个”水桶”容量最短的那块木板。理想情况下HBase 中所有的请求应该均匀的分布在所有RgionServer的所有Region 上,当个别Region收到的读写请求数量大幅超过其它的Region,它所在的Region就有可能成为热点。
图2 RegionServer信息(此图来源网络非百度实际数据)
Noah-TSDB初期曾遇到监控元数据表设计不合理导致热点的问题。当时研发同学收到Noah-TSDB写入模块队列堵塞的业务报警,从Noah监控系统上看到同时间段访问HBase异常明显增长。HBase中的个别RegionServer频繁进行GC,网络 I/O 和磁盘 I/O 密集,操作队列中待执行的请求堆积严重,负载明显高于其它的RegionServer。查看异常RegionServer的日志发现大量请求访问的是同一个Region:”tsdb-meta,*** 1.”。初步定位是由于该Region负载过高,导致它所在的RegionServer成为热点,进而导致系统的吞吐量下降,上游写入模块请求堆积。
tsdb-meta是用来存储监控指标的名称、周期等元信息的表,表中红色填充的行代表其拥有数据量超过正常水平的,表结构如下:
表1 原始tsdb-meta表
分析上面的存储结构,我们可以知道:
同一个监控对象(namespace)的监控指标元信息将会存储在 HBase 表的同一行。
不同监控对象的指标数量不同,将导致行的大小不均匀。
HBase中数据分片是以行为单位,每行的数据存储在同一个Region中,当某一行存储的监控指标数量远大于正常水平时,该行就有可能成为热点。
综上所述,当个别监控对象拥有的监控指标个数过多时,tsdb-meta可能会出现热点问题。同时经我们验证发现,成为热点的监控对象拥有的监控指标的数量大约是正常水平的20倍左右,进一步确认了故障原因。
定位到根因后,我们决定从两个方面来着手解决这个问题。一方面,定期统计监控对象拥有的指标个数,及时发现由于监控配置异常和不合理使用导致的个别监控对象拥有的监控指标过多的问题。第二方面,对tsdb-meta表结构改造,将原来按列分布的数据修改为按行展开平铺,充分打平数据,利用HBase按行自动分片的机制来达到负载均衡的状态。第一方面主要是从业务层面对不合理使用的情况进行人工干预。今天主要着重介绍第二方面。
tsdb-meta表Schema改造
前文大体介绍了表结构改造的思路,避免单行数据过大导致热点问题。我们将监控对象和监控指标的名称信息一起作为行键,只保留一列用于存储指标的其余信息,避免了因单行数据过大导致的热点问题。
表2 优化后的tsdb-meta表
预分区
tsdb-meta表优化后,我们发现生产环境存储数据的 tsdb-data表也存在热点问题。tsdb-data是用来存储监控指标数值的表,生产环境是按时间跨度进行分表,每两天的数据存储在一张表中。数据的行键由数据hash后的特征变量ts_uid和时间基准timestamp_base组成,利用HBase存储时按行键的字典顺序排序的特点,将不同的监控指标的数据散列到不同的Region,相同监控对象的指标数据顺序排列,达到优化查询的效果。由于tsdb-data表的日常访问量基数较大,当某个监控对象拥有的指标数量高于平均水平,那么该监控对象的监控指标很大概率会被分配到相同的Region,导致该Region过大,进成为热点,集群会分裂过大的Region来维持负载均衡的状态。频繁的分裂操作会占用大量资源,影响RegionServer的吞吐量。为解决因Region过大导致的热点,我们采用了对数据表进行预分区的方法。
在对tsdb-data表进行预分区时,我们发现只通过指定Region数量来实现预分区的效果并不理想,因为会出现实际写入量与槽位分配不均的问题。HBase数据表是按照行键的字节空间均匀划分而不是按照实际存储的数据量进行划分。如下图所示,图中红色方块代表实际存储的数据,白色的方块代表无实际数据的行。
图3 原始Region预分区
如上图,虽然数据表已经按照行键的字节空间划分成3个Region了,但是明显Region 3中实际存储的数据量远大于Region 1和Region 2。这种情况下Region 3有成为热点的可能性。为了改善这种情况,Noah-TSDB结合了生产环境中的tsdb-data表按等间隔时间跨度分表的特点,决定参照历史表的使用情况对新表进行预分区。根据生产环境实际产生的行键和预期的分区大小计算出Region分界值,然后根据分界值将表划分成实际水位相近的Region,这样虽然每个Region的槽位大小不一样,但是每个Region实际存储的数量是相当的,进一步降低产生热点的风险。
图4 优化后的Region预分区
如何合理的设置Region数量
在前文介绍的预分区策略中,除了需要参考生产环境的实际使用情况外还需要根据机器资源和分裂阈值等系统参数来预估合适的Region大小,Region大小确定后,我们可以预估出整体的Region数量。那么如何判断当前集群是否能够承载调整后的Region数量呢?如果Region的数量不合理有哪些危害呢?在讨论Region数量对集群的影响之前,我们先了解一些基础知识:
在HBase的数据写入流程中,数据是先写到Memstore(写缓存)排序,然后异步Flush追加到HFile中。一个Region中的多个列族对应多个Memstore,Memstore Flush的最小单位是Region。
当一个RegionServer中所有Memstore的大小总和达到阈值hbase.regionserver.global.memstore.upperLimit * hbase_heapsize会触发Memstore Flush。根据 Memstore从大到小依次Flush,直至MemStore内存使用量低于阈值 hbase_heapsize * hbase.regionserver.global.memstore.lowerLimit。
HBase 会定期Flush Memstore来保障Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行Flush导致的问题,定期的Flush操作有随机延时。
综上可知,一方面由于同一个RegionServer共享Memstore,Region数量过多会导致Memstore Flush的频率变快,产生的HFile变多,HBase持续的进行 Compaction,引发合并风暴。另一方面由于HBase定期Flush Memstore,每次执行 Flush需要将每个Region的每个列族对应的Memstore写入文件并存到HDFS, Region数量越多,每次需要一起处理的文件数量就越大,即使存在随机时延机制,短时间内文件的创建和数据的迁移较多也会加大集群负载,可能引起快照超时、客户端超时和批量加载超时,降低TSDB系统性能。因此Region数量过多会降低系统吞吐量。
Region数量过少也会降低系统性能。当数据量不变的情况下,由于Region数量过少导致单个Region过大,每个Region处理的写入请求数偏高,当Flush的速度慢慢被写入速度追上并赶超时,就会堵塞写入,影响RPC,进而影响HBase的整体写入和查询,降低系统的吞吐量。
Region数量设置不合理,会降低TSDB系统整体性能与可靠性,一般推荐的单个RegionServer管理的Region数量计算方法如下:
#{Region} = (RS memory)*(total memstore fraction)/((memstore size)*(# {column families}))
举个例子,如果RegionServer的参数如下:
Java Heap Size of HBase RegionServer in Bytes设置的是20G
hbase.regionserver.global.memstore.upperLimit是0.4
hbase.hregion.memstore.flush.size是128M
多个表的列族个数共2个
那么 #{Region} = 20 * 1024 * 0.4/ (128 * 2) = 32。这个公式是在假设所有的Region都在以相同的速率写的前提下,如果实际只有部分Region在写入数据,结果可以根据比例、结合业务进行调整。例如Noah-TSDB的场景下,数据是按照时间分表,一般两天的数据存在一张数据表中,数据的写入都集中在最近的一张表,因此实际写入活跃的Region数量远小于Region的总数量,所以实际每个RegionServer管理的Region的数量大约是通过上述公式直接计算结果的3倍左右。
预估出整体的Region数量和单个RegionServer管理的Region数量后,就可以合理的进行容量规划,在集群调整的时候预估需要的机器资源。
总 结上面就是今天介绍的全部内容了,给大家简单分享了一些使用HBase的实践经验。其实在实际使用时我们也发现了HBase过重,运维成本较高等问题,也在持续的进行调研和架构升级,大家有什么好的建议欢迎不吝赐教。另外文中如果有理解不到位或者偏差的地方,欢迎大家指正。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31557835/viewspace-2638517/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/31557835/viewspace-2638517/