---
title: github-fill
description: 优化器动态采样 OceanBase 数据库 V4.2.0 版本开始支持优化器动态采样功能，该功能在 SQL 运行时可以收集需要的统计信息，帮助优化器生成更好的执行计划，从而优化查询性能。OceanBase 数据库的 SQL 查询语句都会自动应用动态采样功能，使计划的生成更加精准，执行计划的执行更加高效。 原理介绍 动…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 4.5.0

# 优化器动态采样

更新时间：2026-02-18 09:40:14

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V4.5.0/zh-CN/700.reference/1000.performance-tuning-guide/500.sql-optimization/400.sql-optimization/400.optimizer-statistics/200.statistics-collection-methods/600.optimizer-dynamic-sampling.md)  

OceanBase 数据库 V4.2.0 版本开始支持优化器动态采样功能，该功能在 SQL 运行时可以收集需要的统计信息，帮助优化器生成更好的执行计划，从而优化查询性能。OceanBase 数据库的 SQL 查询语句都会自动应用动态采样功能，使计划的生成更加精准，执行计划的执行更加高效。

## 原理介绍

动态采样不仅可以使优化器得到足够多的统计信息，还会在计划生成阶段针对数据库对象进行提前采样，通过采样的方式进行行数估计，从而用于代价模型中，生成更好的执行计划。

OceanBase 数据库的动态采样功能具有如下的优点：

- 动态采样可以基于陈旧的统计信息，甚至在缺乏信息的情况下获得更准确的统计信息。
 - 动态采样可以在包含复杂谓词、关联谓词等查询中提供更加准确的统计信息。
 - 动态采样可以减少统计数据收集的时间和成本，例如对于大宽表，常规的统计信息收集可能会非常耗时和资源。
 - 动态采样可以提高查询的时效性，因为动态采样可以在查询时动态调整执行计划，以适应数据的变化。

在执行 SQL 查询时，OceanBase 数据库优化器需要收集表和索引的统计信息，以便选择最佳的执行计划。如果统计信息不准确或者不完整，使用的执行计划就可能不是最优的，导致查询性能下降。基础的统计信息通常是通过自动收集或者手动收集等方式获取。但是，如果数据分布发生变化、没有收集统计信息或者遇到一些复杂的 SQL 查询场景，统计信息就可能不再准确。

如下示例中创建的两张表 `t1` 和 `t2` 都具有 1000 行数据，假设两个表都没有收集统计信息。

```shell
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` 的执行计划情况如下：

```shell
+---------------------------------------------------------------------------------------------+
| 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`，整个计划执行性能也会更快。如下示例中的查询计划的执行性能会更好。

```shell
+---------------------------------------------------------------------------------------------+
| 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 数据库优化器的动态采样的原理，本文通过如下具体示例进行说明。

```shell
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。

```shell
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` 用于开启和关闭动态采样功能。语法如下：

```shell
/* 开启 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 用于控制查询是否使用动态采样。语法如下：

```shell
/*+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` 采用动态采样功能：

```shell
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：

```shell
obclient [oceanbase]> ALTER SYSTEM SET _optimizer_ads_time_limit = 100;
Query OK, 0 rows affected

```

## 使用建议

动态采样为了使优化器得到足够多的统计信息，会在计划生成阶段针对数据库对象进行提前采样，通过采样的方式进行行数估计，从而用于代价模型中，生成更好的计划。

动态采样丰富了优化器获取统计信息的手段，在统计信息不可用或者无法提供准确的行数估计时，提供一种更好的解决方案。但是在使用动态采样功能的时候，需要注意以下几点：

- 由于动态采样默认使用的是块采样，因此建议导入数据之后做相应的转储合并变更，以获得更好的采样效果。
 - 动态采样不可避免的会带来部分硬解析时间上的额外开销，如果部分 TP 场景的业务如果无法承受，则可以选择关闭该功能。
 - 动态采样仅仅只是作为统计信息收集的一种补充手段，业务场景不要完全依赖于该功能，基础的统计信息收集还是需要做的。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
