OceanBase ObTableScan 算子实现源码导读

主要流程

OB 中的每个算子都定义了 inner_openinner_get_next_rowinner_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 &param,
                                           ObNewRowIterator *&result)

close

inner_close 执行扫尾工作,释放一些扫描用的数据结构。

要点

  • open 阶段会做扫描初始化,生成数据流的 iterator。但是要注意,这个阶段并不会真的去读存储层的数据,而是把封装了读逻辑的 iterator 初始化好。
  • next 阶段会真正驱动 iterator 去读数据,并把数据返回到 SQL 层。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值