首批通过分布式安全可靠测评,为关键业务系统打造
优化器动态采样
更新时间:2026-02-18 09:40:14
OceanBase 数据库 V4.2.0 版本开始支持优化器动态采样功能,该功能在 SQL 运行时可以收集需要的统计信息,帮助优化器生成更好的执行计划,从而优化查询性能。OceanBase 数据库的 SQL 查询语句都会自动应用动态采样功能,使计划的生成更加精准,执行计划的执行更加高效。
原理介绍
动态采样不仅可以使优化器得到足够多的统计信息,还会在计划生成阶段针对数据库对象进行提前采样,通过采样的方式进行行数估计,从而用于代价模型中,生成更好的执行计划。
OceanBase 数据库的动态采样功能具有如下的优点:
- 动态采样可以基于陈旧的统计信息,甚至在缺乏信息的情况下获得更准确的统计信息。
- 动态采样可以在包含复杂谓词、关联谓词等查询中提供更加准确的统计信息。
- 动态采样可以减少统计数据收集的时间和成本,例如对于大宽表,常规的统计信息收集可能会非常耗时和资源。
- 动态采样可以提高查询的时效性,因为动态采样可以在查询时动态调整执行计划,以适应数据的变化。
在执行 SQL 查询时,OceanBase 数据库优化器需要收集表和索引的统计信息,以便选择最佳的执行计划。如果统计信息不准确或者不完整,使用的执行计划就可能不是最优的,导致查询性能下降。基础的统计信息通常是通过自动收集或者手动收集等方式获取。但是,如果数据分布发生变化、没有收集统计信息或者遇到一些复杂的 SQL 查询场景,统计信息就可能不再准确。
如下示例中创建的两张表 t1 和 t2 都具有 1000 行数据,假设两个表都没有收集统计信息。
CREATE TABLE t1(c1 INT, c2 INT, c3 INT);
CREATE TABLE t2(c1 INT, c2 INT, c3 INT);
CREATE INDEX idx_c1 ON t2(c1);
INSERT INTO t1 SELECT level,level,level FROM DUAL CONNECT BY level<=1000;
INSERT INTO t2 SELECT level,level,level FROM DUAL CONNECT BY level<=1000;
查询 SELECT * FROM t1, t2 WHERE t1.c1 = t2.c1 AND t1.c2 = 1000 的执行计划情况如下:
+---------------------------------------------------------------------------------------------+
| Query Plan |
+---------------------------------------------------------------------------------------------+
| ==================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| -------------------------------------------------------------------- |
| |0 |HASH JOIN | |98 |199 | |
| |1 |├─TABLE SCAN |T1 |10 |44 | |
| |2 |└─TABLE SCAN |T2 |1000 |61 | |
| ==================================================================== |
可以看到上述两表 Join 的方式选择的是 HASH JOIN。但是真实情况是,满足 t1.c2 = 1000 的数据只有一行,此时,可以选择走 NESTED-LOOP JOIN,将连接条件 t1.c1 = t2.c1 下压到基表 t2 上,从而 t2 表也可以选择索引 idx_c1,整个计划执行性能也会更快。如下示例中的查询计划的执行性能会更好。
+---------------------------------------------------------------------------------------------+
| Query Plan |
+---------------------------------------------------------------------------------------------+
| ==================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| -------------------------------------------------------------------- |
| |0 |NESTED-LOOP JOIN | |1 |21 | |
| |1 |├─TABLE FULL SCAN |T1 |1 |2 | |
| |2 |└─DISTRIBUTED TABLE RANGE SCAN|T2(IDX_C1)|1 |18 | |
| ====================================================================
由此,我们需要动态采样这样的技术手段来辅助获取更准确的统计信息,帮助优化器选择更好的执行计划。
为了更好的理解 OceanBase 数据库优化器的动态采样的原理,本文通过如下具体示例进行说明。
CREATE TABLE t1(c1 INT, c2 INT, c3 INT, c4 INT);
CREATE INDEX idx_c1 ON t1(c1);
CREATE INDEX idx_c2 ON t1(c2);
Q1: SELECT c4 FROM t1 WHERE c1 > 1 AND c2 > 1 AND c3 > 1 GROUP BY c4;
Q2: SELECT /*+PARALLEL(2)*/c4 FROM t1 WHERE c1 > 1 AND c2 > 1 AND c3 > 1 GROUP BY c4;
针对上述示例中的查询,首先基表路径会生成以下三条路径:
- 主表路径
- 索引表路径
idx_c1 - 索引表路径
idx_c2
然后动态采样基于上述三条路径构建采样 SQL 如下:
- 基表的行数估计:
count(*)。 c4列的基础统计信息:approx_count_distinct(c4)、sum(case when c4 is null then 1 else 0 end)。- 满足所有谓词的行数估计:
sum(case when c1 > 1 and c2 > 1 and c3 > 1 then 1 else 0 end)。 - 满足索引表路径
idx_c1的 Query Range 行数估计:sum(case when c1 > 1 then 1 else 0 end)。 - 满足索引表路径
idx_c2的 Query Range 行数估计:sum(case when c2 > 1 then 1 else 0 end)。
同时基于微块个数计算其采样率,假设 Q1 查询的采样率为 ratio,则 Q2 查询的采样率为 2*ratio。构建如下两条采样 SQL。
DYNAMIC SAMPLING Q1:
SELECT
/*+ NO_REWRITE
NO_PARALLEL
DYNAMIC_SAMPLING(0)
QUERY_TIMEOUT(1000000)
*/
count(*),
approx_count_distinct("C4"),
Sum(CASE WHEN "C4" IS NULL THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C1" > 1 ) AND ( "C2" > 1 ) AND ( "C3" > 1 ) THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C1" > 1 ) THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C2" > 1 ) THEN 1 ELSE 0 END)
FROM "TEST"."T1" SAMPLE BLOCK(ratio) SEED(seed);
DYNAMIC SAMPLING Q2:
SELECT
/*+ NO_REWRITE
PARALLEL(64)
DYNAMIC_SAMPLING(0)
QUERY_TIMEOUT(1000000)
*/
count(*),
approx_count_distinct("C4"),
Sum(CASE WHEN "C4" IS NULL THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C1" > 1 ) AND ( "C2" > 1 ) AND ( "C3" > 1 ) THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C1" > 1 ) THEN 1 ELSE 0 END),
Sum(CASE WHEN ( "C2" > 1 ) THEN 1 ELSE 0 END)
FROM "TEST"."T1" SAMPLE BLOCK(2*ratio);
上述示例中的专有名词含义如下:
NO_REWRITE:不需要走改写路径,属于基表扫描,没有必要采样。NO_PARALLEL:不开启并行,由于原始 SQL 未显式指定,默认不走并行采样。PARALLEL(x):并行采样,如果原始 SQL 指定了并行度,采样 SQL 也会开启并行采样。DYNAMIC_SAMPLING(0):优化器在生成执行计划时禁止使用动态采样。QUERY_TIMEOUT:采样 SQL 的执行时间上限。
动态采样的应用场景
目前动态采样功能默认生效于当前用户的 SQL,当前只支持基表的动态采样。在没有关闭动态采样功能时,以下场景会尝试在计划生成阶段使用动态采样:
- 没有统计信息的场景。
- 查询条件中存在复杂谓词的场景,例如
c1 like '%test%',无法用选择率计算公式进行行数估计。 - 用户指定使用动态采样的场景。
动态采样的控制手段
目前,OceanBase 数据库支持使用系统变量、查询 Hint 及系统配置项三种方式进行动态采样功能的控制,同时动态采样的采样集大小受限于并行度的控制。
系统变量控制动态采样
系统变量 optimizer_dynamic_sampling 用于开启和关闭动态采样功能。语法如下:
/* 开启 GLOBAL 级别的动态采样功能 */
SET GLOBAL optimizer_dynamic_sampling = 1;
/* 开启 SESSION 级别的动态采样功能 */
SET SESSION optimizer_dynamic_sampling = 1;
SET optimizer_dynamic_sampling = 1;
/* 关闭 GLOBAL 级别的动态采样功能 */
SET GLOBAL optimizer_dynamic_sampling = 0;
/* 关闭 SESSION 级别的动态采样功能 */
SET SESSION optimizer_dynamic_sampling = 0;
SET optimizer_dynamic_sampling = 0;
查询 Hint 控制动态采样
DYNAMIC_SAMPLING Hint 用于控制查询是否使用动态采样。语法如下:
/*+DYNAMIC_SAMPLING( [[ qb_name_option ] table_name ] INTNUM1 [, INTNUM2 ] ) */
DYNAMIC_SAMPLING Hint 语法中的字段含义如下:
qb_name_option:Query Block 的名称(可选)。table_name:控制动态采样的表名(可选项,如果为空则表示整个查询使用动态采样)。INTNUM1:指定采样的 Level(目前只支持 0 或者 1;1 表示开启动态采样,0 表示关闭动态采样)。INTNUM2:指定采样的微块数(建议设置 32 个微块以上)。
如下示例为查询时对表 t1 采用动态采样功能:
SELECT /*+DYNAMIC_SAMPLING(t1 1)*/ c4 FROM t1 WHERE c1 > 1 AND c2 > 1 AND c3 > 1 GROUP BY c4;
系统配置项控制动态采样
当前动态采样的最大可用查询时间是当前查询时间的 1/10,例如一个查询的超时时间是 10s,那么动态采样的最大时间是 1s。同时为了防止动态采样的时间过大,OceanBase 数据库支持使用内部系统配置项 _optimizer_ads_time_limit 来控制动态采样的时间上限,默认时间上限是 10s,取值范围为 [0, 300]。如果配置项的值为 0,则会关闭动态采样功能。
如下示例为设置当前租户下的动态采样的时间上限为 100:
obclient [oceanbase]> ALTER SYSTEM SET _optimizer_ads_time_limit = 100;
Query OK, 0 rows affected
使用建议
动态采样为了使优化器得到足够多的统计信息,会在计划生成阶段针对数据库对象进行提前采样,通过采样的方式进行行数估计,从而用于代价模型中,生成更好的计划。
动态采样丰富了优化器获取统计信息的手段,在统计信息不可用或者无法提供准确的行数估计时,提供一种更好的解决方案。但是在使用动态采样功能的时候,需要注意以下几点:
- 由于动态采样默认使用的是块采样,因此建议导入数据之后做相应的转储合并变更,以获得更好的采样效果。
- 动态采样不可避免的会带来部分硬解析时间上的额外开销,如果部分 TP 场景的业务如果无法承受,则可以选择关闭该功能。
- 动态采样仅仅只是作为统计信息收集的一种补充手段,业务场景不要完全依赖于该功能,基础的统计信息收集还是需要做的。