HBase WAL 解析

HBase WAL 解析WAL Write Ahead Log 是 HBase 的 RegionServer 在处理数据插入和删除的过程中用来记录操作内容的一种日志 大致过程如下图所示 首先客户端启动一个操作来修改数据 每一个修改都封装到 KeyValue 对象实例中 并通过 RPC 调用发送到含有匹配 Region 的 HRegionServe 一旦 KeyValue 到达 它们就会被发送管理相应行的 HRegion 实例 数据被写到 WAL 然后被

WAL(Write-Ahead-Log)是HBase的RegionServer在处理数据插入和删除的过程中用来记录操作内容的一种日志。大致过程如下图所示,首先客户端启动一个操作来修改数据,每一个修改都封装到KeyValue对象实例中,并通过RPC调用发送到含有匹配Region的HRegionServer。一旦KeyValue到达,它们就会被发送管理相应行的HRegion实例。数据被写到WAL,然后被放入到实际拥有记录的存储文件的MemStore中。同时还会检查MemStore是否满了,如果满了就会被刷写到磁盘中去。

这里写图片描述

SKIP_WAL:不写wal日志,这种可以较大提高写入的性能,但是会存在数据丢失的危险,只有在大批量写入的时候才使用(出错了可以重新运行),其他情况不建议使用。 ASYNC_WAL:异步写入 SYNC_WAL:同步写入wal日志文件,保证数据写入了DataNode节点。 FSYNC_WAL: 目前不支持了,表现是与SYNC_WAL是一致的 USE_DEFAULT: 如果没有指定持久化级别,则默认为USE_DEFAULT, 这个为使用HBase全局默认级别(SYNC_WAL) 

WAL写入:

对每个wal的写入使用的是多生产者单消费者的模式,这里使用到了disruptor框架,将WALKeyWALEdit信息封装为FSWALEntry,然后通过RingBufferTruck放入RingBuffer中。

接下来看hlog的写入流程,分为以下3步:

日志写入缓存:由rpcHandler将日志信息写入缓存ringBuffer. 缓存数据写入文件系统:每个FSHLog有一个线程负责将数据写入文件系统(HDFS) 数据同步:如果操作的持久化级别为(SYNC_WAL或者USE_DEFAULT 则需进行数据同步处理 

下面来详细说明一下各类线程是如何配合来实现这几步操作的,

rpcHandler线程负责将日志信息(FSWALEntry)写入缓存RingBbuffer,在操作日志写完后,rpcHandler会调用wal的sync方法,进行数据同步,其实际处理为写入一个SyncFuture到RingBuffer,然后blocking一直到syncFuture处理完成。 wal线程从缓存RingBuffer中取数据,如果为日志(FSWALEntry)就调用Writer将数据写入文件系统,如果为SyncFuture,则由专门的同步线程来进行同步处理。 整体处理流程图如下: 

这里写图片描述

HLog的写入

wal写入文件系统是通过Writer来写入的,其实际类为ProtobufLogWriter,使用的是Protobuf的格式持久化处理。使用Protobuf格式有如下优势:

性能较高 结构更加紧凑,节省空间 方便扩展以及支持其他语言,通过其他语言来解析日志。 

写入的日志中是按WALKey和WALEdit来依次存储的(具体内容见前面WALKey和WALEdit类的说明),另外还将WALKey和WALEdit分别进行了压缩处理。

WAL同步过程

class RingBufferEventHandler implements EventHandler 
  
    , LifecycleAware { 
   private 
   final SyncRunner [] syncRunners; 
   private 
   final SyncFuture [] syncFutures; 
   private 
   volatile 
   int syncFuturesCount = 
   0; 
   private 
   volatile SafePointZigZagLatch zigzagLatch; 
   private Exception exception = 
   null; 
   private 
   int syncRunnerIndex; RingBufferEventHandler( 
   final 
   int syncRunnerCount, 
   final 
   int maxHandlersCount) { 
   this.syncFutures = 
   new SyncFuture[maxHandlersCount]; 
   this.syncRunners = 
   new SyncRunner[syncRunnerCount]; 
   for ( 
   int i = 
   0; i < syncRunnerCount; i++) { 
   this.syncRunners[i] = 
   new SyncRunner( 
   "sync." + i, maxHandlersCount); } } ...... } 
   private 
   class SyncRunner extends HasThread { 
     
   private 
   volatile 
   long sequence; 
   private 
   final BlockingQueue 
   
     syncFutures; 
    private 
    volatile SyncFuture takeSyncFuture = 
    null; SyncRunner( 
    final String name, 
    final 
    int maxHandlersCount) { 
    super(name); 
    this.syncFutures = 
    new LinkedBlockingQueue 
    
      (maxHandlersCount * 
     3); } ...... } 
     
    
  

在处理ringBuffer中的syncFuture时,不是每有一个就提交到syncRunner处理,而是按批来处理的,这里的批分2种情况:

从ringBuffer中取到的一批数据(为提高效率,在disruptor框架中是按批从ringBuffer中取数据的,具体的请看disruptor的相关文档),如果这批数据中的syncFuture个数<{hbase.regionserver.handler.count默认值200},则按一批处理,如果这一批数据中的syncFuture个数>={hbase.regionserver.handler.count默认值200}个数,则按{hbase.regionserver.handler.count默认值200}分批处理。 

如果达到了批大小,就从syncRunner数组中顺序选择下一个SyncRunner,将这批数据插入该SyncRunner的BlockingQueue中。最后由SyncRunner线程进行hdfs文件同步处理。为保证数据的不丢失,rpc请求需要保证wal日志写入成功后才能返回,这里HBase做了一系列的优化处理的操作。

WAL滚动

SyncRunner线程在处理日志同步后,如果有异常发生,就会调用requestLogRoll发起日志滚动请求 SyncRunner线程在处理日志同步后, 检查当前在写的wal的日志大小是否超过配置{hbase.regionserver.hlog.blocksize默认为hdfs目录块大小}*{hbase.regionserver.logroll.multiplier默认0.95},超过后同样调用requestLogRoll发起日志滚动请求 每个RegionServer有一个LogRoller线程会定期滚动日志,滚动周期由参数{hbase.regionserver.logroll.period默认值1个小时}控制 

这里前面2种场景调用requestLogRoll发起日志滚动请求,最终也是通过LogRoller来执行日志滚动的操作。

WAL失效

当memstore中的数据刷新到hdfs后,那对应的wal日志就不需要了,FSHLog中有记录当前memstore中各region对应的最老的sequenceId,如果一个日志中的各个region的操作的最新的sequenceId均小于wal中记录的各个需刷新的region的最老sequenceId,说明该日志文件就不需要了,于是就会将该日志文件从./WALs目录移动到./oldWALs目录。这块是在前面日志滚动完成后调用cleanOldLogs来处理的。

WAL删除

由于wal日志还会用于跨集群的同步处理,所以wal日志失效后并不会立即删除,而是移动到oldWALs目录。由HMaster中的LogCleaner这个Chore线程来负责wal日志的删除,在LogCleaner内部通过参数{hbase.master.logcleaner.plugins}以插件的方式来筛选出可以删除的日志文件。目前配置的插件有ReplicationLogCleaner、SnapshotLogCleaner和TimeToLiveLogCleaner

TimeToLiveLogCleaner: 日志文件最后修改时间在配置参数{hbase.master.logcleaner.ttl默认600秒}之前的可以删除 ReplicationLogCleaner:如果有跨集群数据同步的需求,通过该Cleaner来保证那些在同步中的日志不被删除 SnapshotLogCleaner: 被表的snapshot使用到了的wal不被删除 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209036.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午10:11
下一篇 2026年3月19日 上午10:12


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号