codecamp

HBase:WAL拆分

WAL拆分

RegionServer服务于许多区域。区域服务器中的所有区域共享相同活动的WAL文件。WAL文件中的每个编辑都包含有关它属于哪个区域的信息。当打开区域时,需要重播属于该区域的WAL文件中的编辑。因此,WAL文件中的编辑必须按区域分组,以便可以重播特定的集合以重新生成特定区域中的数据。按区域对WAL编辑进行分组的过程称为日志拆分。如果区域服务器出现故障,它是恢复数据的关键过程。

在群集启动时由HMaster完成日志拆分,或者在区域服务器关闭时由ServerShutdownHandler完成日志拆分。为保证一致性,受影响的区域在数据恢复之前不可用。所有WAL编辑都需要在给定区域再次可用之前恢复并重播。因此,受到日志拆分影响的区域在该过程完成之前不可用。

过程:日志分割,分步执行

新目录按以下模式命名:

  1. /hbase/WALs/<host>,<port>,<startcode>目录被重新命名。重命名该目录非常重要,因为即使HMaster认为它已关闭,RegionServer仍可能启动并接受请求。如果RegionServer没有立即响应,也没有检测到它的ZooKeeper会话,HMaster可能会将其解释为RegionServer失败。重命名日志目录可确保现有的有效WAL文件仍然由活动但繁忙的RegionServer使用,而不会意外写入。新目录根据以下模式命名:
    /hbase/WALs/<host>,<port>,<startcode>-splitting       

    这种重命名的目录的例子可能如下所示:

    /hbase/WALs/srv.example.com,60020,1254173957298-splitting
  2. 每个日志文件都被拆分,每次一个。日志拆分器一次读取一个编辑项的日志文件,并将每个编辑条目放入对应于编辑区域的缓冲区中。同时,拆分器启动多个编写器线程。编写器线程选取相应的缓冲区,并将缓冲区中的编辑项写入临时恢复的编辑文件。临时编辑文件使用以下命名模式存储到磁盘:
    /hbase/<table_name>/<region_id>/recovered.edits/.temp
    该文件用于存储此区域的WAL日志中的所有编辑。日志拆分完成后,.temp文件将被重命名为写入文件的第一个日志的序列ID。要确定是否所有编辑都已写入,将序列ID与写入HFile的上次编辑的序列进行比较。如果最后编辑的序列大于或等于文件名中包含的序列ID,则很明显,编辑文件中的所有写入操作都已完成。
  3. 日志拆分完成后,每个受影响的区域将分配给RegionServer。打开该区域时,会检查recoverededed文件夹以找到恢复的编辑文件。如果存在任何这样的文件,则通过读取编辑并将其保存到MemStore来重播它们。在重放所有编辑文件后,MemStore的内容被写入磁盘(HFile),编辑文件被删除。

处理日志分割期间的错误

如果您将该hbase.hlog.split.skip.errors选项设置为true,则错误处理如下:

  • 拆分过程中遇到的任何错误都将被记录。
  • 有问题的WAL日志将被移到hbase rootdir下的.corrupt目录中,
  • WAL的处理将继续进行

如果该hbase.hlog.split.skip.errors选项设置为false默认值,则将传播该异常,并将该拆分记录为失败。

拆分崩溃的RegionServer的WAL时如何处理EOFException

如果在拆分日志时发生EOFException,即使hbase.hlog.split.skip.errors设置为false,拆分也会继续。在读取要拆分的文件集合中的最后一个日志时,可能会出现EOFException,因为RegionServer可能在崩溃时写入记录的过程中。

在日志分割期间的性能改进

WAL日志拆分和恢复可能需要大量资源并需要很长时间,具体取决于崩溃中涉及的RegionServer的数量和区域的大小。启用或禁用分布式日志分割是为了提高日志分割期间的性能。

启用或禁用分布式日志拆分

分布式日志处理自HBase 0.92开始默认启用。该设置由hbase.master.distributed.log.splitting属性控制,可以设置为true或false,但默认为true。

分布式日志拆分,分步执行

配置分布式日志拆分后,HMaster控制进程。HMaster在日志拆分过程中注册每个RegionServer,实际拆分日志的工作由RegionServers完成。分布式日志拆分中逐步描述的日志拆分的一般过程在这里仍然适用。

  1. 如果启用分布式日志处理,则HMaster会在集群启动时创建拆分日志管理器实例。
    • 拆分日志管理器管理所有需要扫描和拆分的日志文件。
    • 拆分日志管理器将所有日志作为任务放入ZooKeeper splitWAL节点( hbase/splitWAL)中。
    • 您可以通过发出以下zkCli命令来查看splitWAL的内容。显示示例输出。
      ls /hbase/splitWAL
      [hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2FWALs%2Fhost8.sample.com%2C57020%2C1340474893275-splitting%2Fhost8.sample.com%253A57020.1340474893900,
      hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2FWALs%2Fhost3.sample.com%2C57020%2C1340474893299-splitting%2Fhost3.sample.com%253A57020.1340474893931, 
      hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2FWALs%2Fhost4.sample.com%2C57020%2C1340474893287-splitting%2Fhost4.sample.com%253A57020.1340474893946]
      
      输出包含一些非ASCII字符。解码后,它看起来更简单:
      [hdfs://host2.sample.com:56020/hbase/WALs
      /host8.sample.com,57020,1340474893275-splitting
      /host8.sample.com%3A57020.1340474893900,
      hdfs://host2.sample.com:56020/hbase/WALs
      /host3.sample.com,57020,1340474893299-splitting
      /host3.sample.com%3A57020.1340474893931,
      hdfs://host2.sample.com:56020/hbase/WALs
      /host4.sample.com,57020,1340474893287-splitting 
      /host4.sample.com%3A57020.1340474893946]
      
      该列表表示要扫描和拆分的WAL文件名,这是日志拆分任务的列表。
  2. 拆分日志管理器监视日志拆分任务和工作人员。拆分日志管理器负责以下正在进行的任务:
    • 一旦拆分日志管理器将所有任务发布到splitWAL znode,它就会监视这些任务节点并等待它们被处理。
    • 检查是否有任何排队等待的分组日志工人。如果发现没有响应的工作人员所要求的任务,它将重新提交这些任务。如果由于某些ZooKeeper异常而导致重新提交失败,则无法使用的工作者将再次排队等待重试。
    • 检查是否有未分配的任务。如果找到,它会创建一个短暂的重新扫描节点,以便通知每个拆分的日志工作者通过nodeChildrenChangedZooKeeper事件重新扫描未分配的任务。
    • 检查已分配但过期的任务。如果发现任何东西,它们会再次返回到TASK_UNASSIGNED状态,以便它们可以重试。这些任务可能被分配给缓慢的工作人员,或者他们可能已经完成。这不是问题,因为日志拆分任务具有幂等性。换句话说,相同的日志拆分任务可以被处理多次而不会引起任何问题。
    • 拆分日志管理器不断监视HBase拆分日志节点。如果任何拆分日志任务节点数据发生更改,拆分日志管理器将检索节点数据。节点数据包含任务的当前状态。您可以使用该zkCli get命令来检索任务的当前状态。在下面的示例输出中,输出的第一行显示任务当前未分配。
      get /hbase/splitWAL/hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2FWALs%2Fhost6.sample.com%2C57020%2C1340474893287-splitting%2Fhost6.sample.com%253A57020.1340474893945
      
      unassigned host2.sample.com:57000
      cZxid = 0×7115
      ctime = Sat Jun 23 11:13:40 PDT 2012 
      ...
      
                  
      根据数据更改的任务的状态,拆分日志管理器将执行以下操作之一:
      • 如果任务未分配,请重新提交;
      • 如果任务被分配,则Heartbeat;
      • 如果任务失败,请重新提交或失败;
      • 如果任务完成时出现错误,请重新提交或失败;
      • 如果任务由于错误而无法完成,则请重新提交或失败;
      • 如果任务成功完成或失败,请将其删除。
    任务失败的原因:该任务已被删除;该节点不再存在;日志状态管理器无法将任务的状态移至TASK_UNASSIGNED;重新提交的次数超过了重新提交阈值。
  3. 每个RegionServer的拆分日志工作器执行日志拆分任务。
    每个RegionServer运行一个称为拆分日志工作器的守护进程线程,它负责拆分日志。守护程序线程在RegionServer启动时启动,并注册自己以观察HBase znode。如果任何splitWAL znode子项发生更改,它会通知睡眠工作器线程唤醒并获取更多任务。如果工作人员当前任务的节点数据发生更改,则工作人员将检查该任务是否已由其他工作人员执行。如果是这样,工作线程会停止当前任务的工作。工作人员不断监视splitWAL znode。出现新任务时,拆分日志工作人员将检索任务路径并检查每个任务路径,直到找到未声明的任务,并尝试声明该任务。如果声明成功,它将尝试执行该任务并state根据拆分结果更新任务的属性。此时,拆分日志工作者会扫描另一个无人认领的任务。拆分日志工作者如何接近任务
    • 它查询任务状态,只在任务处于TASK_UNASSIGNED状态时采取行动。
    • 如果任务处于TASK_UNASSIGNED状态,则工作人员尝试TASK_OWNED自行设置状态。如果它没有设置状态,另一名工人将尝试抓住它。如果任务保持未分配,拆分日志管理器还会要求所有工作人员稍后重新扫描。
    • 如果工作人员成功地完成任务,它会尝试再次获取任务状态,以确保它真正异步获取它。同时,它启动一个拆分任务执行器来完成实际工作:
      • 获取HBase根文件夹,在根目录下创建一个临时文件夹,并将日志文件拆分为临时文件夹。
      • 如果拆分成功,任务执行程序将任务设置为状态TASK_DONE。
      • 如果工作人员捕获到意外的IOException,则该任务将设置为状态TASK_ERR。
      • 如果工作人员正在关闭,请将任务设置为状态TASK_RESIGNED。
      • 如果任务是由另一名工作人员完成的,则只需登录即可。
  4. 拆分日志管理器监视未完成的任务。拆分日志管理器在所有任务成功完成时返回。如果所有任务都完成并出现一些故障,则拆分日志管理器将引发异常,以便日志拆分可以重试。由于异步实现,在极少数情况下,拆分日志管理器会丢失一些已完成任务的跟踪。因此,它定期检查其任务图或ZooKeeper中剩余的未完成任务。如果没有找到,它会抛出一个异常,以便日志拆分可以马上重试,而不是挂在那里等待不会发生的事情。
HBase:MultiWAL支持
HBase:WAL压缩
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

HBase快速入门

HBase批量加载

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }