本文档旨在提供一个关于冻结与转储流程所对应的日志内容指南。
冻结流程
冻结过程涉及到以下组件:ObService、ObTenantFreezer、ObFreezer。
在 ObService 级别,所有冻结命令的开始与结束会有对应的日志:
LOG_INFO("receive minor freeze request", K(arg));
LOG_INFO("finish minor freeze request", K(ret), K(arg), K(cost_ts));
租户级冻结
租户级冻结开始的关键日志如下,调用顺序:ObService -> ObTenantFreezer -> ObFreezer:
/* ObTenantFreezer */
// 租户级冻结开始
FLOG_INFO("[TenantFreezer] tenant_freeze start", K(ret));
/* ObFreezer */
// 普通日志流级冻结
FLOG_INFO("[Freezer] logstream_freeze start", K(ret), K_(ls_id));
// 强制日志流级冻结
FLOG_INFO("[Freezer] force_logstream_freeze start", K(ret), K_(ls_id));zer] force_logstream_freeze start", K(ret), K_(ls_id));
租户级冻结结束的关键日志如下,回调顺序:ObFreezer-> ObTenantFreezer -> ObService :
/* ObFreezer */
// 日志流冻结成功
FLOG_INFO("[Freezer] logstream_freeze success", K(ret), K_(ls_id),
K(freeze_clock));
// 日志流冻结失败
TRANS_LOG(WARN, "[Freezer] logstream_freeze failure", K(ret), K_(ls_id));
// 强制日志流冻结成功
FLOG_INFO("[Freezer] force_logstream_freeze success", K(ret), K_(ls_id),
K(freeze_clock));
// 强制日志流冻结失败
LOG_WARN("[Freezer] force_logstream_freeze failure", K(ret), K_(ls_id));
/* ObTenantFreezer */
// 租户级冻结成功
LOG_INFO("[TenantFreezer] succeed to freeze tenant", K(ret), K(ls_cnt),
K(is_force_freeze));
// 租户下某个日志流冻结失败
LOG_WARN("[TenantFreezer] fail to freeze logstream", K(ret), K(ls->get_ls_id()),
K(is_force_freeze));
/* ObService */
// 租户级冻结成功
LOG_INFO("succeed to freeze tenant", K(tenant_id), K(ret));
分区级冻结
分区级冻结开始的关键日志如下,调用顺序:ObService -> ObTenantFreezer -> ObFreezer:
/* ObTenantFreezer */
// 分区级冻结开始
FLOG_INFO("[TenantFreezer] tablet_freeze start", K(ret), K(tablet_id));
/* ObFreezer */
// 普通分区级冻结
FLOG_INFO("[Freezer] tablet_freeze start", K(ret), K_(ls_id), K(tablet_id));
// 强制分区级冻结
FLOG_INFO("[Freezer] force_tablet_freeze start", K(ret), K_(ls_id), K(tablet_id));
分区级冻结结束的关键日志如下,回调顺序:ObFreezer -> ObTenantFreezer -> ObService :
/* ObFreezer */
// 分区冻结成功
FLOG_INFO("[Freezer] tablet_freeze success", K(ret), K_(ls_id), K(tablet_id));
// 强制分区冻结成功
FLOG_INFO("[Freezer] force_tablet_freeze success", K(ret), K_(ls_id), K(tablet_id));
/* ObTenantFreezer */
// 分区级冻结成功
LOG_INFO("[TenantFreezer] succeed to freeze tablet", K(ret), K(ls_id), K(tablet_id));
// 分区级冻结失败
LOG_WARN("[TenantFreezer] fail to freeze tablet", K(ret), K(ls_id), K(tablet_id));
/* ObService */
// 分区级冻结成功
LOG_INFO("succeed to freeze tablet", K(ret), K(tenant_id), K(tablet_id));
其它重要日志
其他与冻结流程相关的重要日志分布在 ObFreezer 和 ObMemtable 级别。
ObFreezer 级别
// 有正在执行的冻结,退出此次冻结 FLOG_INFO("[Freezer] freeze is running", K(ret), K_(ls_id)); // 等待正在执行的冻结完成 TRANS_LOG(WARN, "[Freezer] waiting for the running freeze to end", K_(ls_id), K(cost_time); // 过久等待提交日志 TRANS_LOG(ERROR, "[Freezer] failed to traverse trans ctx to submit log", K(ret), K_(ls_id), K(cost_time)); // 日志流冻结,过久等待data_chekpoint完成冻结 TRANS_LOG(WARN, "[Freezer] finish ls_freeze costs too much time", K_(ls_id), K(cost_time); // 分区级冻结,不需要冻结该分区 TRANS_LOG(INFO, "[Freezer] no need to freeze since there is no active memtable", K(ret), K_(ls_id), K(tablet_id)); // 强制分区级冻结,创建空memtable失败 LOG_WARN("[Freezer] fail to create an active memtable for force_tablet_freeze", K(ret), K_(ls_id), K(tablet_id)); // (强制)分区级冻结,过久等待memtable ready_for_flush TRANS_LOG(WARN, "[Freezer] ready_for_flush costs too much time", K_(ls_id), K(cost_time)); TRANS_LOG(INFO, "[ObMemtable] print_ready_for_flush", KP(this), K(ls_id), K(tablet_id), K(ret), K(bool_ret), K(frozen_memtable_flag), K(write_ref), K(unsynced_cnt), K(current_right_boundary), K(end_log_ts), K(logstream_freeze_clock), K(memtable_freeze_clock)); // (强制)分区级冻结,memtable是否被移到prepare_list中等待被转储 TRANS_LOG(WARN, "[Freezer] memtable cannot be flushed", K(ret), K_(ls_id), K(*memtable)); TRANS_LOG(INFO, "[Freezer] memtable is ready to be flushed", K(ret), K_(ls_id), K(*memtable)); // 一些重要ts TRANS_LOG(TRACE, "[Freezer] get_max_decided_log_ts", K(ret), K_(ls_id), K(max_decided_log_ts)); TRANS_LOG(TRACE, "[Freezer] get_ls_weak_read_ts", K(ret), K_(ls_id), K(weak_read_ts)); TRANS_LOG(TRACE, "[Freezer] get_ls_weak_read_ts", K(ret), K_(ls_id), K(weak_read_ts));ObMemtable 级别
// 满足转储条件 TRANS_LOG(INFO, "ready for flush", K(bool_ret), K(ret), K(current_right_boundary), K(ls_id), K(*this)); // 创建转储任务 TRANS_LOG(INFO, "flush memtable", K(ret), K(param), KPC(this));
转储流程
转储前会先创建转储 DAG,成功创建 DAG 后加到 DagScheduler 的等待队列里,等待调度即可。 值得一提的是,同一个Tablet的转储是串行的,即同一时刻一个 Tablet 最多只有一个转储 DAG 存在。 OceanBase 数据库 V4.x 版本上 Tablet 发起转储的入口目前只有一个:ObMemtable::flush()。
// 创建转储任务
TRANS_LOG(INFO, "flush memtable", K(ret), K(param), KPC(this));
ret = 0,表示 Tablet 成功创建并添加了转储 DAG。ret = -4023(EAGAIN),表示 Tablet 已经存在一个未结束的转储 DAG。- 如果没有该条日志,说明对应的 Tablet 没有发起转储。
转储 DAG 会依次执行三类 Task:ObTabletMergePrepareTask -> ObTabletMergeTask -> ObTabletMergeFinishTask 。
PrepareTask
ObTabletMergePrepareTask::process,主要进行转储前的一些准备工作,如拿 TabletHandle,筛选需要参加本次转储的冻结 Memtable 等。
关键日志:
// 成功创建 merge ctx,prepare 阶段任务完成
FLOG_INFO("succeed to init merge ctx", "task", *this);
// 转储失败,K(ctx) 会打印 tablet ID 等信息
FLOG_WARN("sstable merge finish", K(ret), K(ctx), "task", *(static_cast<ObITask *>(this)));
MergeTask
ObTabletMergeTask::process,该阶段从冻结 Memtable 中迭代多版本行,并将 fuse row 写到宏块里。
关键日志:
// 迭代 memtable,刷宏块结束
FLOG_INFO("merge macro blocks ok", K(idx_), "task", *this);
// 转储失败
STORAGE_LOG(WARN, "failed to merge partition", K(ret));
FinishTask
ObTabletMergeFinishTask::process,该阶段进行转储收尾工作,输出转储 SSTable,更新 table store,释放 Memtable,更新 Tablet 等。
关键日志:
// 转储任务失败,可以根据该条日志搜索 Tablet 转储失败的 Trace
FLOG_WARN("sstable merge finish", K(ret), K(ctx), "task", *(static_cast<ObITask *>(this)));
// 转储成功
FLOG_WARN("sstable merge finish", K(ret), K(ctx), "task", *(static_cast<ObITask *>(this)));
冻结转储相关的虚拟表
__all_virtual_tablet_compaction_history:查找 tablet 上的历史转储信息,包括 mini merge, minor merge 和 major merge。__all_virtual_compaction_diagnose_info:快速查找转储失败的 Trace。__all_virtual_dag:当前 OBServer 节点中所有的 DAG 信息,可以查看转储队列的积压情况。__all_virtual_tenant_memstore_info:查看各租户的内存使用情况。
注意
如果是 farm 环境,日志量比较少的情况下,可以直接搜索 dag_ret:- 或 sstable merge finish.*tablet_id" 来寻找失败的转储 Trace。
适用版本
OceanBase 数据库 V4.x 版本。