任务调度
OceanBase 线程管理是封装好的,支持如下多种不同模式的线程管理:
// deps/oblib/src/lib/thread/thread_mgr.henum 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.cppint ObStoreFile::open(const bool is_physical_flashback){int ret = OB_SUCCESS;bool is_replay_old = false;// 打开StoreFile,并做相关初始化的动作...// 执行首次GCenable_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 {// ...
垃圾回收和坏块检查线程的生命周期和 OBServer 进程是一致的,它们都会在 ObStoreFile::destroy 中停止。
宏块的垃圾回收
宏块垃圾回收的任务定义如下:
// 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.cppvoid 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 {// 标识开始进行gcset_mark_sweep_doing();// 1. 采用bitmap对所有在使用的宏块进行打标// a. data块的打标是通过一个宏块迭代器实现的// b. meta块的打标比较简单,因为可用的meta块是记录在一个数组中的// 经过打标之后我们就得到所有有效的宏块的bitmapif (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 happenSTORAGE_LOG(ERROR, "the macro block is freed, ", K(i));}} else {// 无用的宏块,进行GCif (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();}}
坏块检查
坏块检查任务也会在 StoreFileGC 线程做,该任务也是一个定时任务,它的定义如下:
class ObFileSystemInspectBadBlockTask : public common::ObTimerTask {public:// 继承至ObTimerTask,主要执行函数在inspect_bad_blockvirtual 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.henum ObMacroBlockCheckLevel {CHECK_LEVEL_NOTHING = 0,// 不做坏块检查CHECK_LEVEL_MACRO, // 检查宏块的checksum,如果宏块的checksum不存在直接返回成功CHECK_LEVEL_MICRO, // 检查微块的checksumCHECK_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中的bloomfilterint check_data_header(const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf,const int64_t macro_block_buf_size, const ObFullMacroBlockMeta& meta);// 检查普通宏块的具体数据,遍历宏块中每一个微块,微块的检查见下面的check_micro_dataint 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);// 检查微块的具体数据,包括其中每一行的每一列的checksumint check_micro_data(const char* micro_buf, const int64_t micro_buf_size, const ObFullMacroBlockMeta& meta, int64_t* checksum);// 检查sstable宏块headerint check_sstable_data_header(const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);// 检查LOB宏块headerint check_lob_data_header(const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);// 检查bloomfilter中的headerint check_bloomfilter_data_header(const ObMacroBlockCommonHeader& common_header, const char* macro_block_buf, const ObFullMacroBlockMeta& meta);};
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




