Code前端首页关于Code前端联系我们

为什么HBase需要序列ID?——基本点理解

terry 2年前 (2023-09-26) 阅读数 45 #数据库

为什么需要序列ID?

HBase数据写入时,先添加到Hlog,然后写入Memstore。这意味着某些数据以两种格式写入。不同的形式存在于两个地方。两个地方的相同数据是否需要一种机制将两者关联起来?有的朋友想问为什么要把两者关联起来,所以笔者在这里提出三个相关的问题:

1。 Memstore中的数据 HLog对应的数据传输到HDFS文件后是否可以删除?否则,HLog将会无限增长!那么问题来了,如何将Memstore中传输到HDFS的数据与Hlog中对应的日志数据进行映射?

2。 HBase中单个HLog的大小是固定的,日志文件的最大数量也是固定的。默认情况下,HLog 文件的最大数量为 8。如果日志数量超过此数量,则必须删除最旧的 HLog。这就产生了问题,怎么知道要删除的HLog日志对应的数据都已经放到了呢? (如果知道哪些数据未放置,可以强制填充,然后清除Hlog)

3。 RegionServer宕机后,Memstore中的数据必然会丢失。大家都知道可以通过HLog来恢复。那么问题来了,HLog中哪些数据需要恢复呢?哪些数据不需要恢复?

这三个问题本质上是同一个问题。所有这些都需要一个媒介来表明Memstore数据Flush中的哪个点对应于哪个Hlog位置,这个媒介就是本文的重点 - SequenceId

HLog 基本结构

要了解SequenceId,需要简单了解一下HBase中HLog文件的基本结构,如下图所示。主要有两个问题:HBase为什么需要sequenceId?——一定要弄懂的原理

1。每个RegionServer有一个或多个HLog(默认只有1个,版本可以启用MultiWAL功能,允许多个HLog)。每个 HLog 分为几个区域。如图所示,Region A、Region B、Region C共享一个HLog文件。

2。 HLog 中的日志单元 WALentry 表示行级更新的最小附加单元(图中的红色/黄色小框)。它由两部分组成:HLogKey和WALEdit。 HLogKey包含了几个属性信息,包括表名、区域名、序列ID等; WALEdit用于表示事务中包含的更新集。行级事务可以对同一行中的多个列进行原子操作。在上图中,WALEdit 包含几个键值。

什么是序列标识符?

序列号是区域级别行级事务的升序序列号。我做了这个定义。需要注意三件事:

1。 Sequenceid是一个自增序列号。很容易理解,就是说随着时间的推移,它会增加,不会减少。

2。序列标识符是行级事务的自增序列号。什么是行级事务?简单地说,它按顺序更新多个列族和列。行级事务可以保证更新的一致性、一致性、持久性和隔离性。 HBase 会自动为行级事务分配一个递增的序列号。

3。序列码是区域级别自动递增的序列号。每个region都维护自己的sequenceid,不同region的sequenceid相互独立。

在这样的定义条件下,HLog如下: HBase为什么需要sequenceId?——一定要弄懂的原理

HLog包含两个区域的日志记录。框中的数字代表序列代码。随着时间的推移,各个地区的排名都是独立提升的。

1。问:HLog什么时候可以过期并回收?

下图中边框右侧部分是超过单个HLog大小阈值后创建的HLog文件。问题是,系统什么时候可以回收并删除这个文件呢? 。理论上,直到该文件所有区域对应的最大序列都排完为止,就可以删除了。例如下图中,如果将A区域对应的最大序列码放入(5),则B区域对应的最大序列码也放入(5)。 ,则可以删除Hlog。它是如何实施的? HBase为什么需要sequenceId?——一定要弄懂的原理

RegionServer 为每个 Region 维护了一个代表该 Region 的变量oldestUnflushedSequenceId(实际上,对于每个 store,目前为了清晰起见,将其视为一个 Region)。尚未上线的最早的seqid,即该seqid上线之前的所有数据。接下来,我们将看看 flush 下如何保存这个值,以及如何使用这个值来实现 HLog 过期回收决策。

下图是flush过程中最旧的变量UnflushedSequenceId的变化示意图。它最初为零。假设第二阶段 A 区(红框)在某个时间点处决了flush。中间是HLog序列ID 1-4对应的信息。已下订单。在运行 flush 之前,HBase 在 HLog 中添加一个空条目以获取下一个序列 ID (5),并将其sequenceId 设置为 OldestUnflushedSequenceId-RegionA。如图所示,第三步中的OldestUnflushedSequenceId-RegionA指向sequenceId为5的记录。HBase为什么需要sequenceId?——一定要弄懂的原理

可以看到,每次flush之后,这个变量都会向前移动一定的距离。这个变量至关重要,是解决文章开头提到的三个问题的关键。基于上面对这个变量的理解,我们看看以下两种场景是否可以删除正确的HLog: HBase为什么需要sequenceId?——一定要弄懂的原理

显然,第一种场景中,还有一些数据还没有放到正确的HLog中(序列ID= 5尚未放置)),因此无法删除它;而第二种场景,右侧HLog中的数据全部放到磁盘上,这样HLog理论上就可以被删除并复用。

2。问题:如果HLog数量超过阈值(maxlogs)后最早的HLog被删除,哪些区域需要强制更新?

假设当前系统设置HLog的最大数量为32,即上图最左边的HLog为33,此时系统会检索最旧的日志(最右边的HLog),并检查数据是否正确对应的所有条目均已落地。从图中可以看到,C区还有一些数据没有落地。为了安全起见,删除这个HLlog,这必须在该区域强制执行一次flush操作,并将所有数据放到磁盘上。HBase为什么需要sequenceId?——一定要弄懂的原理

问题3:如果RegionServer宕机并恢复重放日志,哪些WALEntry应该重放,哪些WALEntry被跳过?

理论上,只有未落地Memstore的数据对应的WALEntry才需要重放,已经落地的数据对应的WALentry才可以重放。跳过。但问题是RegionServer宕机了,对应的信息肯定丢失了。我们应该做什么?找到一种方法来保存它。上面分析的最古老的 UnflushgedSequenceId 变量是flush时期生成的。这个变量可以作为flush手下的变量。将数据形式写入到HFile中(见下面的代码): HBase为什么需要sequenceId?——一定要弄懂的原理

这样在区域停机迁移重新开放后,可以加载HFile元数据来恢复核心变量oldestUnflushgedSequenceId(在flush生成的所有HFlies中)这次,它们都存储相同的序列ID)。恢复后,此序列 ID 可用于过滤在重放 WALentry 时要重放哪些条目以及要跳过哪些条目。

这里有一个问题:有没有可能flush创建的所有H文件同时存储的序列ID不一致?例如:区域内所有商​​店(store1、store2)执行flush,store1成功执行flush。目前oldestUnflush变量edSequenceId已经成功添加到对应的HF中;然而,RegionServer 在 store2 执行 flush 之前崩溃了。 Store2对应的最旧的UnPutting edSequenceId变量仍然是前一个文件对应的sequenceId。这样的话,对播放数据有影响吗?如果是这样,为什么?如果不是,什么机制可以保证这一点?

至此,以上所有分析都基于一个事实:flush 在 hbase 中的操作是一个 Region 级别的操作,即 flush 每次执行都需要整个 Region 的所有 Store。全部被flush处决。接下来,对Per-CF Flush比较感兴趣的可以继续阅读作为延伸阅读。 Per-CF Flush 允许系统对一个或某些列组单独执行冲洗。实现原理与上面分析的基本类似。不同的是,上一篇文章中最旧的 UnflushedSequenceId 与区域一一匹配。在Per-CF Flush中,该参数必须为每个store指定,并且与store一一对应。

延伸阅读:Per-CF Flush

在地区层面,flush存在很多问题。对于多个列族,其中一个存储大小超过阈值(1.28 亿)。其他店铺无论大小,都必然会倒下。磁盘上,一些非常小的列族(几兆字节)写入磁盘后形成许多非常小的文件,不利于从hbase中读取。

per-cf flush 允许商店执行 flush。该功能在上述版本中已经存在,并且被设置为该版本中的默认规则。实现此功能需要完成两个任务。一是为flush提出新的战略,可能为flush选择一个或多个独立的柱族。当前的新策略称为FlushLargerStoresPolicy,意思是刷新最大的。商店里有flush。第二个是,需要从区域到存储指定oldestUnflushedSequenceId粒度,即卡到卡

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门