OceanBase 源码解读(十二):宏块的垃圾回收和坏块检查

简介: 此前,OceanBase 源码解读第十一篇《Location Cache 模块浅析》,为大家介绍了 observer 上的一个基础模块,为 SQL、事务、CLOG 等多个其他模块提供获取及缓存某个副本位置信息的能力的 Location Cache 模块。本期“源码解读”继续由 OceanBase 技术专家公祺为大家带来“存储层代码解读之「宏块的垃圾回收和坏块检查」”。


在解读之前,大家可以先回顾一下公祺的上篇关于《
存储层代码解读之「宏块存储格式」》的内容。通过走读 OceanBase 宏块的代码,我们对其存储格式有了初步的了解,知道了微块作为读 I/O 最小单元,宏块是写 I/O 的最小单元。

今天我们来考考大家,OceanBase 的垃圾回收(GC)和坏块检查是以微块还是宏块作为单位呢?答案很明显,就是宏块。因为既然是以宏块为单位写,所以肯定是以宏块为单位进行清理和检查的。和本专题前几篇文章类似,本文主要通过走读相关代码,了解宏块垃圾回收和坏块检查的原理。

* 注:本文所有的说明及代码都是基于 v3.1.0_CE_BP1(“阅读原文”直达)版本的 OceanBase 开源代码。


image.png

// deps/oblib/src/lib/thread/thread_mgr.h
enum class TGType {
  INVALID,
  THREAD_POOL,    // 线程池
  OB_THREAD_POOL,  // OB线程池
  TIMER,      // 定时任务
  TIMER_GROUP,    // 定时任务组
  QUEUE_THREAD,    // 队列线程调度
  DEDUP_QUEUE,    // 去重队列调度
  ASYNC_TASK_QUEUE,  // 异步任务队列调度
};

OceanBase 宏块的垃圾回收和坏块检查主要采用 TIMER 定时任务调度,TIMER 的主要特点是启动一个后台线程,周期性的对任务进行调度。关于其他的调度方式,后续会单独介绍。


既然采用 TIMER 调度,那么其调度的时机也很明显,在启动 OBServer 进程时,就启动了一个后台线程,周期性的进行垃圾回收和坏块检查,代码如下(省略了部分和垃圾回收无关的代码)。

// src/storage/blocksstable/ob_store_file.cpp
int ObStoreFile::open(const bool is_physical_flashback)
{
  int ret = OB_SUCCESS;
  bool is_replay_old = false;
  // 打开StoreFile,并做相关初始化的动作
  ...
  // 执行首次GC
  enable_mark_sweep();
  mark_and_sweep();
  // 启动GC定时任务线程
  // 调度GC的任务
  // 调度坏块检查的任务
  if (OB_FAIL(TG_START(lib::TGDefIDs::StoreFileGC))) {
    STORAGE_LOG(WARN, "The timer has not been inited, ", K(ret));
  } else if (OB_FAIL(TG_SCHEDULE(lib::TGDefIDs::StoreFileGC, gc_task_, RECYCLE_DELAY_US, true))) {
    STORAGE_LOG(WARN, "Fail to schedule gc task, ", K(ret));
  } else if (OB_FAIL(TG_SCHEDULE(lib::TGDefIDs::StoreFileGC, inspect_bad_block_task_, INSPECT_DELAY_US, true))) {
    STORAGE_LOG(WARN, "Fail to schedule bad_block_inspect task, ", K(ret));
  } else {
  // ...

image.png

// src/storage/blocksstable/ob_store_file.h
// ObTimerTask为定时任务的基类
class ObStoreFileGCTask : public common::ObTimerTask {
public:
  ObStoreFileGCTask();
  virtual ~ObStoreFileGCTask();
  // 继承至ObTimerTask,GC的主要执行函数在:ObStoreFile::mark_and_sweep()
  virtual void runTimerTask() 
{
    OB_STORE_FILE.mark_and_sweep();
  }
};

ObStoreFile::mark_and_sweep 主要逻辑是对所有在用的宏块进行打标,并检查未打标的宏块,将其中 ref_cnt_ 为 0 的进行垃圾回收。

// src/storage/blocksstable/ob_store_file.cpp
void ObStoreFile::mark_and_sweep()
{
  int ret = OB_SUCCESS;
  MacroBlockId macro_id;
  bool is_freed = false;
  // 检查GC任务的开关
  if (!is_mark_sweep_enabled()) {
    STORAGE_LOG(INFO, "mark and sweep is disabled, do not mark and sweep this round");
  } else {
    // 标识开始进行gc
    set_mark_sweep_doing();
    // 1. 采用bitmap对所有在使用的宏块进行打标
    //   a. data块的打标是通过一个宏块迭代器实现的
    //   b. meta块的打标比较简单,因为可用的meta块是记录在一个数组中的
    // 经过打标之后我们就得到所有有效的宏块的bitmap
    if (OB_FAIL(mark_macro_blocks())) {
      STORAGE_LOG(WARN, "Fail to mark macro blocks, ", K(ret));
    } 
    // 2. 遍历所有的宏块,进行垃圾清理
    begin_time = end_time;
    if (OB_SUCC(ret)) {
      for (int64_t i = 0; i < store_file_system_->get_total_macro_block_count() && is_mark_sweep_enabled(); ++i) {
        if (bitmap_test(i)) {
          // 有效的宏块,做个二次检查
          if (macro_block_info_[i].is_free_) {
            // BUG, should not happen
            STORAGE_LOG(ERROR, "the macro block is freed, ", K(i));
          }
        } else {
          // 无用的宏块,进行GC
          if (0 == ATOMIC_LOAD(&(macro_block_info_[i].ref_cnt_))) {
            // ref为0,即可进行清理
            if (!macro_block_info_[i].is_free_) {
              // 释放宏块
              //   a. 将该宏块信息进行标注:is_free_=true
              //   b. 将该宏块id放入到free_block_array_中,后续进行二次重用
              free_block((uint32_t)i, is_freed);
              // ...
            }
          }
        }
      }
    }
    set_mark_sweep_done();
  }
}

image.png

坏块检查任务也会在 StoreFileGC 线程做,该任务也是一个定时任务,它的定义如下:

class ObFileSystemInspectBadBlockTask : public common::ObTimerTask {
public:
  // 继承至ObTimerTask,主要执行函数在inspect_bad_block
  virtual void runTimerTask()
{
     inspect_bad_block();
  }
private:
  // 遍历所有的宏块,通过check_macro_block进行宏块检查
  // 由于坏块检查比较耗费资源,inspect_bad_block中也做一些流控逻辑
  void inspect_bad_block();
  // 对某1个宏块进行检查,检查的主要内容有:
  //   a. 宏块id、宏块元数据
  //   b. 通过check_data_block对宏块数据进行深度检查
  int check_macro_block(const ObMacroBlockInfoPair& pair, const storage::ObTenantFileKey& file_key);
  int check_data_block(const MacroBlockId& macro_id, const blocksstable::ObFullMacroBlockMeta& full_meta,
      const storage::ObTenantFileKey& file_key);
private:
  // 工具类,宏块检查主要函数,后面会详细分析
  ObSSTableMacroBlockChecker macro_checker_;
};

坏块检查的代价普遍较高,因为可能需要从磁盘读取所有宏块的内容,并进行 checksum 校验,会占用大量的 CPU、I/O 能力,进而影响业务流量。所以,OceanBase 的坏块检查支持不同级别的检查,DBA 可以根据系统自身的特点,比如磁盘、CPU 的具体配置等,选取合适的级别进行坏块检查,支持级别具体如下。

// src/storage/blocksstable/ob_macro_block_checker.h
enum ObMacroBlockCheckLevel {
  CHECK_LEVEL_NOTHING = 0,// 不做坏块检查
  CHECK_LEVEL_MACRO,      // 检查宏块的checksum,如果宏块的checksum不存在直接返回成功
  CHECK_LEVEL_MICRO,      // 检查微块的checksum
  CHECK_LEVEL_ROW,        // 检查每个column的checksum,会遍历每一行的每一列
  CHECK_LEVEL_AUTO,       // 默认的检查级别,如果宏块的checksum存在,就检查宏块,如果宏块的checksum不存在,就检查微块
  CHECK_LEVEL_MAX
};

接下来我们看下 ObSSTableMacroBlockChecker 的主要内容:

// src/storage/blocksstable/ob_macro_block_checker.h
// note: 该class中的函数是线程不安全的
class ObSSTableMacroBlockChecker {
public:
  // check是宏块检查主要函数,会根据check_level进行不同级别的检查
  // 具体内容的检查见private中的函数
  int check(const char* macro_block_buf, const int64_t macro_block_buf_size, const ObFullMacroBlockMeta& meta,
      ObMacroBlockCheckLevel check_level = CHECK_LEVEL_AUTO);
private:
  // 检查宏块数据buffer的checksum,并和header中checksum中做比较
  int check_macro_buf(
      const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const int64_t macro_block_buf_size);
  // 检查宏块数据的header,具体的检查内容包括:
  //   a. check_sstable_data_header:检查sstable数据的header
  //   b. check_lob_data_header:检查log数据的header
  //   c. check_bloomfilter_data_header:检查header中的bloomfilter
  int check_data_header(const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf,
      const int64_t macro_block_buf_size, const ObFullMacroBlockMeta& meta);
  // 检查普通宏块的具体数据,遍历宏块中每一个微块,微块的检查见下面的check_micro_data
  int check_data_block(const char* macro_block_buf, const int64_t macro_block_buf_size,
      const ObFullMacroBlockMeta& meta, const bool need_check_row);
  // 检查LOB宏块的具体数据,包括其中所有的微块
  int check_lob_block(
      const char* macro_block_buf, const int64_t macro_block_buf_size, const ObFullMacroBlockMeta& meta);
  // 检查微块的具体数据,包括其中每一行的每一列的checksum
  int check_micro_data(
      const char* micro_buf, const int64_t micro_buf_size, const ObFullMacroBlockMeta& meta, int64_t* checksum);
  // 检查sstable宏块header
  int check_sstable_data_header(
      const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);
  // 检查LOB宏块header
  int check_lob_data_header(
      const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);
  // 检查bloomfilter中的header
  int check_bloomfilter_data_header(
      const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);
};

image.png

相关文章
|
6月前
|
SQL 关系型数据库 数据库
OceanBase数据库常见问题之OAT添加服务器预检查的时候报错如何解决
OceanBase 是一款由阿里巴巴集团研发的企业级分布式关系型数据库,它具有高可用、高性能、可水平扩展等特点。以下是OceanBase 数据库使用过程中可能遇到的一些常见问题及其解答的汇总,以帮助用户更好地理解和使用这款数据库产品。
|
SQL 存储 缓存
OceanBase 源码解读(十一):Location Cache 模块浅析
OceanBase 源码解读(十一):Location Cache 模块浅析
335 0
OceanBase 源码解读(十一):Location Cache 模块浅析
|
存储 Java 数据库
OceanBase 源码解读(九):存储层代码解读之「宏块存储格式」
此前,带你读源码第八篇《事务日志的提交和回放》,为大家介绍了日志模块的设计理念和日志的一生。本期“源码解读”由数据库技术专家公祺为大家带来“存储层代码解读之「宏块存储格式」”
473 0
OceanBase 源码解读(九):存储层代码解读之「宏块存储格式」
|
SQL 存储 Java
OceanBase 源码解读(七):一文读懂数据库索引实现原理
此前,带你读源码第六篇《戳这里回顾:OceanBase 源码解读(六):存储引擎详解》为大家详细讲解了 OceanBase 存储引擎,并为大家回答了关于 OceanBase 数据库的相关提问。
720 1
|
存储 缓存 Oracle
OceanBase 源码解读(六):存储引擎详解
从2010年一路走来,每一步 OceanBase 犹如走在悬崖峭壁,走得十分小心翼翼。回头看,非处当时之情景,不能理解当时之设计。好的设计不是“想”出来的,而是“痛”出来的,希望大家在阅读时也能够感受到这份成果背后的“痛并快乐着”。
1353 0
OceanBase 源码解读(六):存储引擎详解
|
SQL 调度 数据库
OceanBase 源码解读(五):租户的一生
此前,带你读源码第四篇《戳这里回顾:OceanBase 源码解读(四):事务的一生》为大家介绍了事务的外部接口相关知识。本文将介绍社区版中创建、删除租户、资源隔离的相关代码。
450 0
OceanBase 源码解读(五):租户的一生
|
3月前
|
存储 SQL 分布式数据库
OceanBase 入门:分布式数据库的基础概念
【8月更文第31天】在当今的大数据时代,随着业务规模的不断扩大,传统的单机数据库已经难以满足高并发、大数据量的应用需求。分布式数据库应运而生,成为解决这一问题的有效方案之一。本文将介绍一款由阿里巴巴集团自主研发的分布式数据库——OceanBase,并通过一些基础概念和实际代码示例来帮助读者理解其工作原理。
309 0
|
1月前
|
SQL 存储 人工智能
OceanBase CTO杨传辉谈AI时代下数据库技术的创新演进路径!
在「DATA+AI」见解论坛上,OceanBase CTO杨传辉先生分享了AI与数据库技术融合的最新进展。他探讨了AI如何助力数据库技术演进,并介绍了OceanBase一体化数据库的创新。OceanBase通过单机分布式一体化架构,实现了从小规模到大规模的无缝扩展,具备高可用性和高效的数据处理能力。此外,OceanBase还实现了交易处理、分析和AI的一体化,大幅提升了系统的灵活性和性能。杨传辉强调,OceanBase的目标是成为一套能满足80%工作负载需求的系统,推动AI技术在各行各业的广泛应用。关注我们,深入了解AI与大数据的未来!
|
3月前
|
Oracle 架构师 分布式数据库
OceanBase数据库的发展历程是什么?
【8月更文挑战第11天】OceanBase数据库的发展历程是什么?
176 63
|
3月前
|
Oracle 关系型数据库 MySQL
OceanBase数据库简介
【8月更文挑战第9天】OceanBase数据库简介
360 60