hbase的MOB文件清理机制

清理MOB文件的线程

MOB表中的mob文件有时候也需要清理,当MOB文件满足如下条件时,需要对其进行清理。

  • MOB文件中的数据已经过期无用,此时该MOB文件就可以被清理删除
  • 若干个小的MOB文件被合并之后,老的MOB文件不再被任何region所引用,此时也可以被清理删除

针对MOB文件的清理,HBase提供了两个线程任务来执行,分别是RSMobFileCleanerChoreMobFileCleanerChore,两者的特点如下。

  • MobFileCleanerChore
    • 调用者:HMaster服务调用执行
    • 范围:针对整个HBase集群的MOB文件进行清理
    • 清理对象: 创建时间超过TTL时间的MOB文件
  • RSMobFileCleanerChore
    • 调用者: RegionServer
    • 范围: 仅清理位于当前RegionServer上的Region所引用的MOB文件
    • 清理对象:没有被任何region引用的MOB文件,属于被清理的对象

下面针对这两个MOB清理任务,分别进行详细说明。

MobFileCleanerChore工作机制

前提条件

只有满足如要条件的列族,MobFileCleanerChore才会对其执行清理操作

  • 列族是MOB类型
  • 列族配置的最小保存版本数为0,如果不为0,则不执行
  • 列族配置了TTL

如下所示。

1
2
3
4
5
6
7
8
9
10
11
// 检查列族是否是MOB类型, 最小保存的版本数是否为0
if (hcd.isMobEnabled() && hcd.getMinVersions() == 0) {
cleaner.cleanExpiredMobFiles(htd.getTableName().getNameAsString(), hcd);
}

// 判断TTL
long timeToLive = columnDescriptor.getTimeToLive();
if (Integer.MAX_VALUE == timeToLive) {
// no need to clean, because the TTL is not set.
return;
}
MobFileCleanerChore的判定条件
获取过期时间

MobFileCleanerChore任务是周期性任务,默认情况下,一天执行一次。
在执行时,会首先获取当前时间current,然后根据列族的TTL时间,计算一个过期时间,然后MOB文件时间在这个过期时间之前的,都认为是已经过期的文件,是可以被删除的。
如现在时间是2024-09-26 12:00:00, TTL是1天,则计算的过期时间是2024-09-26 12:00:00,然后再对时分秒清零,因此过期时间是2024-09-26 00:00:00+000

1
2
3
4
5
6
Calendar calendar = Calendar.getInstance();  
calendar.setTimeInMillis(current - timeToLive * 1000);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

这里需要说明下为什么要对时分秒清零?
因为MOB文件名中包含时间字符串,但是该字符串是不包含时分秒等数据。

获取MOB文件的时间

MOB文件名的格式如下所示。

1
d41d8cd98f00b204e9800998ecf8427e20240925221150b7685c4bf5bcba54c97658e3dd_63f50cb1b677b063afc5bcd56c703e2e

其中文件名字符串的32-39位表示的是该文件的创建时间,HBase通过解析该文件名字符串获取到MOB文件的创建时间。

MOB文件的时间来源

MOB数据,在写入的时候也是会首先写入到Memstore,只是在flush的时候,会将元数据信息存放在hfile中,真实的数据会存储在MOB文件中。
HBase在flush的时候,会根据暂存在memstore中的KV数据的时间戳来确定MOB文件的时间,如果存在多个KV,则以最新的时间戳最为生成MOB文件的时间。

示例:

1
2
3
4
5
6
# 1727061887ms = 1970-01-01 08:00:02
# 1727061887000ms = 2024-09-23 11:24:47
# 1727061889ms = 1970-01-21 07:44:21
put 'mob', 'r1', 'info:name', bigdata_value, 1727061887
put 'mob', 'r2', 'info:name', bigdata_value, 1727061887000
put 'mob', 'r3', 'info:name', bigdata_value, 1727061889

如上所示,我们往mob表中写入了3个KV,其中最新的时间戳是第二个,时间是1727061887000, 因此生成的MOB文件时间就是*20240923*

对比时间

由于MOB文件时间是当前文件中最新KV的时间戳,因此只要该时间过期,那么意味着该文件中的所有数据都是过期的,因此都可以删除。
通过过期时间和MOB文件时间的对比,如果MOB文件时间处于过期时间之前的,则认为过期。

比较时间的时候,忽略时分秒是否会删除未过期的数据?
答案是不会。

原因:
对于MOB过期文件,hbase是允许多存一些时间的。
比较过期时间时,忽略了时分秒,意味着是以天为单位来进行比较的;且比较时是将早于过期时间的MOB文件判定为过期fileDate < 过期时间,这意味着即使当天过期的MOB文件,也会继续保留一段时间,在第二天才会被删除,因此不会删除未过期的时间。

举例说明:
ttl=1s,MOB文件时间是2024-10-20,假设已经过期, MobFileCleanerChore2024-10-20 12:00:00开始运行,计算出的过期时间是2024-10-20 00:00:00,由于fileDate < 过期时间这个条件不成立,因此本次运行不会将其认定为过期。 只有在2024-10-21 00:00:01时刻及之后,该任务运行,才会判定该MOB文件过期。

RSMobFileCleanerChore工作机制

前提条件

只有满足如下如要条件的列族,RSMobFileCleanerChore才会对其执行清理操作

  • 列族是MOB类型

代码如下所示。

1
List<ColumnFamilyDescriptor> list = MobUtils.getMobColumnFamilies(htd);
RSMobFileCleanerChore的执行流程
  1. 获取当前RegionServer上的表,针对每个表,有如下操作
  2. 遍历每个表的Region
  3. 针对每个Region,遍历其所有列族
    1. 找到该region下的MOB列族,遍历列族下的所有hfile文件,并进行如下操作
      1. 读取hfile,获取元数据信息
      2. 元数据信息获取该hfile文件是否引用有MOB文件(原因是MOB列族中也可能写入小数据,此时是不生成MOB文件的)
      3. 如果hfile文件引用有MOB文件,则记录该MOB文件信息;没有继续进行下一个hfile文件的判断
  4. 针对一个表,记录完毕所有满足条件的MOB文件信息
  5. 再次针对这个表的MOB列族,进行如下操作
  6. 遍历每个MOB列族
  7. 针对每个MOB列族,获取对应的mobdir目录下的所有mob文件,针对文件,进行如下操作
    1. 根据文件名信息获取引用该MOB文件的region name
    2. 基于region name判断该region是否在当前的这个RegionServer上,如果不在,跳过该MOB文件;如果在,进行下一步操作
    3. 判断该MOB文件是否被region引用(和上面的记录信息比较),如果存在,则跳过该MOB文件;如果不存在,则说明没有引用,可以进行下一步操作
    4. 判断该MOB文件的最后更新时间是否超过阈值(默认是1h),如果超过,则将其归档清理;不超过,则不归档

总结

虽然HBase中存在两类清理MOB文件的线程,RSMobFileCleanerChoreMobFileCleanerChore
但是两者的作用是完全不同的。

  • MobFileCleanerChore线程,主要用于清理TTL过期的MOB文件,针对的是设置有TTL配置的MOB列族
  • RSMobFileCleanerChore线程,主要用于清理无用的MOB文件,针对的是MOB表中的所有MOB列族。