主要流程
OB 中的每个算子都定义了 inner_open
、 inner_get_next_row
、inner_close
三个方法,他们一般负责算子初始化、计算和吐数据、扫尾。
三个方法之间通过 ObExecContext、ObTaskExecutorCtx 以及算子 Ctx 来传递信息。 由于历史原因,各种 Ctx 之间的区隔比较模糊,特别是 ObExecContext 和 ObTaskExecutorCtx,暂时可以先不去过分纠结,不影响我们对整体的理解。
下面介绍表扫描算子(ObTableScan
)的核心流程。
open
int ObTableScan::inner_open(ObExecContext& ctx) const
do_table_scan()
inline int ObTableScan::do_table_scan(ObExecContext& ctx, bool is_rescan, bool need_prepare /*=true*/) const
ObIDataAccessService* das = NULL;
extract_scan_ranges(*scan_input, *scan_ctx);
get_partition_service(task_exec_ctx, das);
das->table_scan(scan_ctx->scan_param_, scan_ctx->iter_result_);
考虑到扫描的数据源可能有很多种:物理表(数据按照 SSTable 格式存储在磁盘里),虚拟表(数据在内存里),外表(数据在外部,如 OSS、FTP)等等,不同数据源的数据获取方式不一样。
为了屏蔽细节,给他们定义了一个共同的接口:ObIDataAccessService,它提供统一的 table_scan
方法供 ObTableScan
算子使用。
目前 das 背后只有两种实现,虚拟表、物理表:
int ObSQLUtils::get_partition_service(ObTaskExecutorCtx& executor_ctx, int64_t table_id, ObIDataAccessService*& das)
{
int ret = OB_SUCCESS;
if (is_virtual_table(table_id)) {
das = executor_ctx.get_vt_partition_service();
} else {
das = static_cast<ObIDataAccessService*>(executor_ctx.get_partition_service());
}
return ret;
}
不同的 das 对象实现了不同的 table_scan
方法。下一节详述。
get next row
获得了正确的 das 后,执行 table_scan
来做扫描初始化工作。初始化工作成功后,就开始调用 get_next_row
来获取数据。
int ObTableScan::inner_get_next_row(ObExecContext& ctx, const ObNewRow*& row) const
get_next_row_with_mode(scan_ctx, cur_row)))
is_vt_mapping_
和 Oracle 模式下通过虚拟表访问 MySQL 格式的物理表有关,初学者无需理会,可以认为它总是 false。
int ObTableScan::get_next_row_with_mode(ObTableScanCtx* scan_ctx, ObNewRow*& row) const
{
int ret = OB_SUCCESS;
if (is_vt_mapping_) {
// switch to mysql mode
CompatModeGuard g(ObWorker::CompatMode::MYSQL);
ret = scan_ctx->result_->get_next_row(row);
} else {
ret = scan_ctx->result_->get_next_row(row);
}
return ret;
}
从上面可以看到,get_next_row
的数据来自 scan_ctx->result_->get_next_row
接口。
scan_ctx->result_
的类型定义在 ob_table_scan.h
中:common::ObNewRowIterator *result_;
对于一个物理表扫描来说,它实际指向了一个 ObTableScanIterator 对象,该对象封装了从存储层吐给 SQL 层的数据流。详见:
int ObPartitionStorage::table_scan(
ObTableScanParam& param, const int64_t data_max_schema_version, common::ObNewRowIterator*& result)
对于一个虚拟表来说,它实际指向了一个 ObVirtualTableIterator 对象,该对象封装了从虚拟表吐给 SQL 层的数据流。详见:
int ObVirtualDataAccessService::table_scan(ObVTableScanParam ¶m,
ObNewRowIterator *&result)
close
inner_close
执行扫尾工作,释放一些扫描用的数据结构。
要点
- open 阶段会做扫描初始化,生成数据流的 iterator。但是要注意,这个阶段并不会真的去读存储层的数据,而是把封装了读逻辑的 iterator 初始化好。
- next 阶段会真正驱动 iterator 去读数据,并把数据返回到 SQL 层。