首批通过分布式安全可靠测评,为关键业务系统打造
TABLE SCAN
更新时间:2026-05-08 11:04:13
TABLE SCAN 算子是存储层和 SQL 层的接口,用于展示优化器选择哪个索引来访问数据。
在 OceanBase 数据库中,对于普通索引,索引的回表逻辑是封装在 TABLE SCAN 算子中的;而对于全局索引,索引的回表逻辑由 TABLE LOOKUP 算子完成。
示例:含 TABLE SCAN 算子的执行计划
obclient> CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT, c3 INT, c4 INT,
INDEX k1(c2,c3));
Query OK, 0 rows affected
Q1:
obclient> EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1 = 1\G
*************************** 1. row ***************************
Query Plan:
==================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
----------------------------------
|0 |TABLE GET|t1 |1 |46 |
==================================
Outputs & filters:
-------------------------------------
0 - output([t1.c1(0x7f4cbfde31f0)], [t1.c2(0x7f4cbfde35b0)], [t1.c3(0x7f4cbfde38b0)], [t1.c4(0x7f4cbfde3bb0)]), filter(nil), rowset=25,
access([t1.c1(0x7f4cbfde31f0)], [t1.c2(0x7f4cbfde35b0)], [t1.c3(0x7f4cbfde38b0)], [t1.c4(0x7f4cbfde3bb0)]), partitions(p0),
is_index_back=false,
range_key([t1.c1(0x7f4cbfde31f0)]), range[1 ; 1],
range_cond([t1.c1(0x7f4cbfde31f0) = 1(0x7f4cbfde2ad0)])
Q2:
obclient> EXPLAIN EXTENDED SELECT * FROM t1 WHERE c2 < 1 AND c3 < 1 AND
c4 < 1\G
*************************** 1. row ***************************
Query Plan:
======================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------
|0 |TABLE SCAN|t1(k1)|100 |32932|
======================================
Outputs & filters:
-------------------------------------
0 - output([t1.c1(0x7f4cbfde6000)], [t1.c2(0x7f4cbfde3ae0)], [t1.c3(0x7f4cbfde4b90)], [t1.c4(0x7f4cbfde5c40)]), filter([t1.c3(0x7f4cbfde4b90) < 1(0x7f4cbfde4470)], [t1.c4(0x7f4cbfde5c40) < 1(0x7f4cbfde5520)]),
access([t1.c2(0x7f4cbfde3ae0)], [t1.c3(0x7f4cbfde4b90)], [t1.c4(0x7f4cbfde5c40)], [t1.c1(0x7f4cbfde6000)]), partitions(p0),
is_index_back=true, filter_before_indexback[true,false],
range_key([t1.c2(0x7f4cbfde3ae0)], [t1.c3(0x7f4cbfde4b90)], [t1.c1(0x7f4cbfde6000)]), range(NULL,MAX,MAX ; 1,MIN,MIN),
range_cond([t1.c2(0x7f4cbfde3ae0) < 1(0x7f4cbfde33c0)])
上述示例中,执行计划展示中的 outputs & filters 详细展示了 TABLE SCAN 算子的输出信息如下:
| 信息名称 | 含义 |
|---|---|
| operator | TABLE SCAN 算子的 operator 有两种形式:TABLE SCAN 和 TABLE GET。
|
| name | 选择用哪个索引来访问数据。选择的索引的名字会跟在表名后面,如果没有索引的名字,则说明执行的是主表扫描。这里需要注意,在 OceanBase 数据库中,主表和索引的组织结构是一样的,主表本身也是一个索引。更多信息,可以参考 索引的存储。 |
| output | 该算子的输出列。 |
| filter | 该算子的过滤谓词。 由于示例中 Q1 查询的 TABLE GET 算子没有设置 filter,所以为 nil。 |
| partitions | 查询需要扫描的分区。 |
| is_index_back | 该算子是否需要回表。 例如,在 Q1 查询中,因为选择了主表,所以不需要回表。在 Q2 查询中,索引列是 (c2,c3), 由于查询需要返回 c4 列,所以需要回表。 |
| filter_before_indexback | 与每个 filter 对应,表明该 filter 是可以直接在索引上进行计算,还是需要索引回表之后才能计算。 例如,在 Q2 查询中,当 filter 为 c3 < 1 时, 可以直接在索引上计算,能减少回表数量;当 filter 为 c4 < 1 时,需要回表取出 c4 列之后才能计算。 |
| range_key/range/range_cond |
|
根据您给出的查询计划输出,is_index_back 参数的解释是关于查询时是否需要从索引扫描回到主表来检索其他不在索引中的列的数据。
在您的示例中,is_index_back 是 true,这意味着查询需要"回表"来获取不在所用索引列中的数据。创建表 t1 时,定义了一个索引 k1,它包含了列 c2 和 c3。查询中要求获取所有列 (SELECT *),并且有三个过滤条件:c2 < 1、c3 < 1 和 c4 < 1。因为 c4 不在索引 k1 中,所以即使 c2 和 c3 的条件可以通过索引来过滤,获取 c4 的值仍然需要回到主表中。
因此,解释中提到的 "在 Q1 查询中,因为选择了主表,所以不需要回表" 是不适用于这个查询的。在此查询中,尽管 c2 和 c3 可以通过索引来过滤,但是由于查询还需要返回不在索引 k1 中的 c4 列,所以查询需要回表。
所以,解释中的这个部分是不正确的。正确的解释是,由于查询需要返回不在索引 k1 中的 c4 列,因此需要回表以取得 c4 的值。这也解释了为什么查询计划使用了 TABLE SCAN 而不是仅仅使用 INDEX RANGE SCAN:因为它需要访问不在索引中的数据列。