21

HBase运维 | 一张表写入异常引起的HBase Replication 队列堆积

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU5OTQ1MDEzMA%3D%3D&%3Bmid=2247487968&%3Bidx=1&%3Bsn=f9167caae74095ace38914b0533f8b97
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
q6byUvQ.png!mobile

线上的接口依赖于HBase主备集群来提供数据读写服务,HBase主备集群之间则通过replication机制来进行数据同步,正常情况下,数据的读写流量都集中于主集群,只有在主集群读写超时或服务不可用时,才会触发熔断机制,读写流量自动切换至备集群。所以,主备集群的replication模式是互为主备。

1. replication不可用

业务方通过phoenix视图查询,反馈备集群的一张表没有数据,这边通过hbase-shell查询后,也发现表是空的,而主集群则是有数据的。

检查replication peer以及该表的REPLICATION_SCOPE,都是正常的,手动在主集群的表里put一条数据,等待许久,备集群依然没有数据正常同步过来。同时,我也在备集群的表里同样put了一条数据,稍候片刻,主集群依然没有数据正常同步过去。按照以往的经验,一方集群写入的数据,在很小的延时下就会被同步至关联的集群中。

在主集群的hbase-shell下使用命令 status 'replicaion' 来查看当前集群复制队列的进度状态,果然,复制队列存在延迟,复制队列中堆积了大量未被发送的数据。按照以往的经验,正常情况下复制队列中的 SizeOfLogQueue 一般为1。

查看备集群的复制状态,同样也有延迟。

此时,CDH中主备集群的各项指标如:IO、CPU、内存等均是正常的,用arthas分析ReplicationSource的相关线程,也看不出有什么异常。

iUF3IvI.png!mobilethread

监控页面中未见异常,唯一有异常的地方是:hbase的WALs目录非常大,数据总量不到20T,WALs目录空间占用就有20T,这个已经是我们这边老生常谈的问题了。

2. 一张表的写入异常导致replication持续报错

标题的结论,是分析了RegionServer的日志后才得出。日志中持续输出如下报错信息:

VNRvUz7.png!mobilelog-error

根据日志中的异常信息,ReplicationSink端,对表COMPANY_INFO:INDUSTRY_FINANCIAL_ANALYSIS写入数据的时候,遇到了类似Region too busy。Store或StoreFile too busy之类的异常,然后导致ReplicationSource端不停地重试发送数据包。

RegionServer一般出现这种异常,十有八九是因为写入的数据量过大,而表的预分区做的不够好。但是,这张表创建的时候已经做了预分区,依然导致了这样的异常。

回顾这张表的数据写入方式,导入历史数据的时候,先走的离线导入方式,由spark生成hfile,最后用bulkload。只是这张表的数据量太大,spark任务出现了数据倾斜,就改为nifi来推,数据由主集群写入,再通过replication机制同步到备集群,只是在推数据的过程中,nifi报错了,但前一部分的数据已经堆积在了复制队列中,等待着后续被发送至备集群。

然后,所有这些未发送的复制队列中的数据,就成了此次异常的导火索。之后,队列中的数据继续发送至备集群,而备集群的表无法承受这样的写入,导致了region too busy的异常,主集群捕获到RemoteException,依旧会不停地尝试,不停地尝试,直至要写入成功。这样的僵局就造成了主备集群之间的复制队列越积越多。

但是主集群的WALs数据目录异常膨胀,不确定是否和复制队列堆积有关。按理说,复制队列堆积只会跟OldWals目录有关,WALs目录中的数据落盘后会被回收线程定时移动到OldWAls目录中,隔一定时间后会被彻底删除。但是,如果集群开启了replication,过期的预写日志只有在主集群确定发送至备集群后,才会被标记为弃用,否则,就会堆积在OldWals目录中。

3. 破局

主备集群数据复制队列堆积,replication不可用,如果直接清空zookeeper上的replication相关的znode path,势必会造成备集群数据丢失,主备数据不一致。解决问题的过程中,也曾尝试删除或禁用写入异常的表,但是没有用,RegionServer的报错依旧持续发生,复制队列依旧越积越多。

我认为最理想的解决方案是,针对该张表堆积在复制通道的待发送数据包,第一,删除zookeeper中replication的状态(类似于标记为偏移量等),让主备同步机制主动跳过这部分异常的待发送数据。这样,数据丢失也仅是针对这张表。但是,并没有找到这样的解决方案,或者是目前的HBase不支持这样的功能?

HBase数据写入时,正常流程是先写预写日志,对于所有表的数据写入或删除操作,都可以视为一个行为,这些只是不停追加至WALs的文件中,所有表的写入行为都是往一个或多个WALs中追加,而不是我理解的每张表独占一个WALs文件。所以,zookeeper中维护的replication的进度状态,无法区分某张表数据写入的复制状态,所以,就无法针对性地选择跳过哪些表的复制。

在没有找到很完美的解决方案之前,我们只能尝试先重启主集群,虽然主集群的WALs目录很大,但主集群重启的过程还算顺利,主集群重启后,WALs目录慢慢减小,直至为0,可以理解,集群重启的过程中,缓存中的数据会全部刷新至磁盘中,所有的预写日志都会被移动至OLdWAls目录之下,事实也的确如此,OLdWAls变为20多T,等待一段时间之后,该目录持续变小,直至减为3T后便不再减少。

RegionServer中上述那张表replication相关的报错依旧在持续,主备集群之间的复制队列依旧在堆积,replication依旧不可用。现在可以确定,OLdWAls目录之中那3T的数据,就是复制队列中堆积的未发送至备集群的数据。如果贸然清空复制队列,至少有3T的数据是无法被同步至备集群的。

此时,我们陷入了进退维谷的境地,没有别的办法,只能通过重建replication的peer,来消除zookeeper中的复制状态,直接跳过这些堆积的预写日志,然后把主集群的表重新克隆至备集群中,把丢数据的风险降到最低。

选择重建peer后,OLdWAls的目录大小减至为0,查看复制队列状态,延迟消失,replication恢复正常,RegionServer日志中的报错消失,剩下的工作就是手动进行数据同步了。

4. 总结

我们集群参与replication的表有一百多张,所有表的复制,走的是同一个复制链路(peer),一旦某张表的数据同步发生异常,将会导致整个复制链路拥堵,造成replication不可用。那么,如果我为每张表都单独创建一个复制链路,就算一个链路存在问题,其余链路依旧会正常运转。之前尝试过这种方式,但又会引起别的问题,比如,短连接过多,zookeeper连接占用过多等问题。

replication在zookeeper中的复制状态,或者说偏移量之类的,是否可以更改?是否可以针对性地跳过一个或若干个待同步的数据包?或者在zookeeper维护复制队列状态的znode path中,手动删除正在卡住的znode path?

此外,在Replication没有延迟,一切正常的情况下,WALs目录的空间占用依旧会持续膨胀,这个异常点貌似与Replication的关系也不大。

学海无涯,在此以作记录,后续继续探索。

5. 参考链接

  • https://hbase.apache.org/2.1/book.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK