首批通过分布式安全可靠测评,为关键业务系统打造
手动收集统计信息
更新时间:2026-03-19 08:18:22
OceanBase 数据库优化器主要通过 DBMS_STATS 包 和 ANALYZE 语句两种方式手动收集统计信息。
说明
OceanBase 数据库当前不支持对统计信息收集时间的预估。因此建议将 ob_query_timeout 设置得足够长,以便完成统计信息的收集。特别是在收集 schema 级别的统计信息时,如果由于超时而中断,已经收集完毕的统计信息将被保留。
使用说明与限制
- 推荐用户在会话(Session)级别调大 ob_query_timeout 变量,这样做仅影响当前会话,不会干扰其他操作。
完整步骤
| 阶段 | 操作 | 命令/视图 |
|---|---|---|
| 准备 | 调大 ob_query_timeout |
SET SESSION ob_query_timeout = 36000000000; |
| 执行 | 调用 DBMS_STATS 或 ANALYZE |
CALL dbms_stats.gather_table_stats(...); |
| 监控 | 查看收集进度与状态 | (G)V$OB_OPT_STAT_GATHER_MONITOR |
| 验证 | 检查统计信息是否写入/是否过期 | DBA_TAB_STATISTICS、DBA_TAB_COL_STATISTICS(以及直方图相关视图) |
| 诊断 | 处理失败或异常 | DBA_OB_TASK_OPT_STAT_GATHER_HISTORY、DBA_OB_TABLE_OPT_STAT_GATHER_HISTORY + 执行计划缓存(Plan Cache)(含刷新/淘汰说明) |
通过 DBMS_STATS 包收集统计信息
OceanBase 数据库优化器支持利用 DBMS_STATS (MySQL 模式)/DBMS_STATS (Oracle 模式) 包收集表级、Schema 级别的统计信息和索引统计信息,分别通过调用存储过程 gather_table_stats、gather_schema_stats 和 gather_index_stats 来完成。
PROCEDURE gather_table_stats (
ownname VARCHAR2,
tabname VARCHAR2,
partname VARCHAR2 DEFAULT NULL,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
block_sample BOOLEAN DEFAULT FALSE,
method_opt VARCHAR2 DEFAULT DEFAULT_METHOD_OPT,
degree NUMBER DEFAULT NULL,
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
cascade BOOLEAN DEFAULT NULL,
stattab VARCHAR2 DEFAULT NULL,
statid VARCHAR2 DEFAULT NULL,
statown VARCHAR2 DEFAULT NULL,
no_invalidate BOOLEAN DEFAULT FALSE,
stattype VARCHAR2 DEFAULT 'DATA',
force BOOLEAN DEFAULT FALSE
);
PROCEDURE gather_schema_stats (
ownname VARCHAR2,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
block_sample BOOLEAN DEFAULT FALSE,
method_opt VARCHAR2 DEFAULT DEFAULT_METHOD_OPT,
degree NUMBER DEFAULT NULL,
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
cascade BOOLEAN DEFAULT NULL,
stattab VARCHAR2 DEFAULT NULL,
statid VARCHAR2 DEFAULT NULL,
statown VARCHAR2 DEFAULT NULL,
no_invalidate BOOLEAN DEFAULT FALSE,
stattype VARCHAR2 DEFAULT 'DATA',
force BOOLEAN DEFAULT FALSE
);
PROCEDURE gather_index_stats (
ownname VARCHAR2,
indname VARCHAR2,
partname VARCHAR2 DEFAULT NULL,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
stattab VARCHAR2 DEFAULT NULL,
statid VARCHAR2 DEFAULT NULL,
statown VARCHAR2 DEFAULT NULL,
degree NUMBER DEFAULT NULL,
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
no_invalidate BOOLEAN DEFAULT FALSE,
force BOOLEAN DEFAULT FALSE,
tabname VARCHAR2 DEFAULT NULL
);
DBMS_STATS.GATHER_TABLE_STATS (
ownname VARCHAR2,
tabname VARCHAR2,
partname VARCHAR2 DEFAULT NULL,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
block_sample BOOLEAN DEFAULT FALSE,
method_opt VARCHAR2 DEFAULT DEFAULT_METHOD_OPT,
degree NUMBER DEFAULT to_degree_type(get_param('DEGREE')),
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
cascade BOOLEAN DEFAULT NULL,
no_invalidate BOOLEAN DEFAULT FALSE,
stattype VARCHAR2 DEFAULT 'DATA',
force BOOLEAN DEFAULT FALSE,
hist_est_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
hist_block_sample BOOLEAN DEFAULT NULL
);
DBMS_STATS.GATHER_SCHEMA_STATS (
ownname VARCHAR2,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
block_sample BOOLEAN DEFAULT FALSE,
method_opt VARCHAR2 DEFAULT DEFAULT_METHOD_OPT,
degree NUMBER DEFAULT NULL,
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
cascade BOOLEAN DEFAULT NULL,
no_invalidate BOOLEAN DEFAULT FALSE,
stattype VARCHAR2 DEFAULT 'DATA',
force BOOLEAN DEFAULT FALSE);
DBMS_STATS.GATHER_INDEX_STATS (
ownname VARCHAR2,
indname VARCHAR2,
partname VARCHAR2 DEFAULT NULL,
estimate_percent NUMBER DEFAULT AUTO_SAMPLE_SIZE,
stattab VARCHAR2 DEFAULT NULL,
statown VARCHAR2 DEFAULT NULL,
degree NUMBER DEFAULT NULL,
granularity VARCHAR2 DEFAULT DEFAULT_GRANULARITY,
no_invalidate BOOLEAN DEFAULT FALSE,
force BOOLEAN DEFAULT FALSE,
tabname VARCHAR2 DEFAULT NULL);
详细的参数解释您可以参考:
- GATHER_TABLE_STATS (MySQL 模式)/GATHER_TABLE_STATS (Oracle 模式)
- GATHER_SCHEMA_STATS (MySQL 模式)/GATHER_SCHEMA_STATS (Oracle 模式)
- GATHER_INDEX_STATS (MySQL 模式)/GATHER_INDEX_STATS (Oracle 模式)
DBMS_STATS 包提供的收集策略会采用默认配置的 Prefs,关于设置 Prefs 的详细信息,请参见 收集策略配置项(Prefs)管理功能。
PROCEDURE GATHER_DATABASE_STATS_JOB_PROC();
示例
DBMS_STATS 包收集统计信息的相关示例如下:
收集用户
user的表tbl1的全局级别的统计信息。CALL dbms_stats.gather_table_stats('user', 'tbl1', granularity=>'GLOBAL', method_opt=>'FOR ALL COLUMNS SIZE AUTO');收集用户
user的表t_part1的分区级别的统计信息,并行度为 64,只收集数据分布不均匀的列的直方图。CALL dbms_stats.gather_table_stats('user', 't_part1', degree=>'64', granularity=>'PARTITION', method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');收集用户
user的表t_subpart1所有的统计信息,并行度为 128,只收集 50% 的数据,所有的列的直方图都由优化器内部决定。CALL dbms_stats.gather_table_stats('user', 't_subpart1', degree=>'128', estimate_percent=> '50', granularity=>'ALL', method_opt=>'FOR ALL COLUMNS SIZE AUTO');
执行 gather_table_stats 后,立即查询 (G)V$OB_OPT_STAT_GATHER_MONITOR 可确认任务是否启动。
表级和索引统计信息收集策略 GRANULARITY 的关联
默认情况下,在收集表统计信息的同时也会收集表上所有索引统计信息,由于索引表上的部分统计信息是可以通过基表的统计信息得到的,包括行数、平均行长(通过对应的几个列计算)等,因此可以加速统计信息的获取,也可以避免二次扫描全表数据。对于本地索引和全局索引两者在 GRANULARITY 上有所区别,如下表所示。
| 指定分区 | GRANULARITY | 全局索引 | 本地索引 |
|---|---|---|---|
| 否 | ALL | GLOBAL 级别 | ALL |
| 否 | AUTO | GLOBAL 级别 | AUTO |
| 否 | DEFAULT | GLOBAL 级别 | DEFAULT |
| 否 | GLOBAL AND PARTITION | GLOBAL 级别 | GLOBAL AND PARTITION |
| 否 | APPROX_GLOBAL AND PARTITION | GLOBAL 级别 | APPROX_GLOBAL AND PARTITION |
| 否 | GLOBAL | GLOBAL 级别 | GLOBAL |
| 否 | PARTITION | GLOBAL 级别 | PARTITION |
| 否 | SUBPARTITION | 不收集 | SUBPARTITION |
| 是 | ALL | GLOBAL 级别 | ALL |
| 是 | AUTO | GLOBAL 级别 | AUTO |
| 是 | DEFAULT | GLOBAL 级别 | DEFAULT |
| 是 | GLOBAL AND PARTITION | GLOBAL 级别 | GLOBAL AND PARTITION |
| 是 | APPROX_GLOBAL AND PARTITION | 不收集 | APPROX_GLOBAL AND PARTITION |
| 是 | GLOBAL | GLOBAL 级别 | GLOBAL |
| 是 | PARTITION | 不收集 | PARTITION |
| 是 | SUBPARTITION | 不收集 | SUBPARTITION |
通过 ANALYZE 语句收集统计信息
在 Oracle 模式和 MySQL 模式下,OceanBase 数据库支持使用 ANALYZE 语句进行统计信息的收集。
MySQL 模式下的 ANALYZE 语法如下:
analyze_stmt:
ANALYZE TABLE table_name UPDATE HISTOGRAM ON column_name_list WITH INTNUM BUCKETS
当租户级配置项 enable_sql_extension 为 TRUE 的时候,可以使用 Oracle 模式的语法,即 OceanBase 数据库为 MySQL 模式提供的扩展语法。
analyze_stmt:
ANALYZE TABLE table_name [use_partition] analyze_statistics_clause
MySQL 模式下 ANALYZE 语句统计信息收集示例如下:
收集表
tbl1的统计信息,列的桶个数为 30 个。ANALYZE TABLE tbl1 UPDATE HISTOGRAM ON a, b, c, d WITH 30 BUCKETS;在 MySQL 模式下使用 Oracle 模式的语法收集用户
test的表tbl1的统计信息。ALTER SYSTEM SET ENABLE_SQL_EXTENSION = TRUE; ANALYZE TABLE tbl1 COMPUTE STATISTICS FOR ALL COLUMNS SIZE AUTO;
Oracle 模式下的 ANALYZE 语法如下:
analyze_stmt:
ANALYZE TABLE table_name [use_partition] analyze_statistics_clause
use_partition:
PARTITION (parition_name [,partition_name...])
| SUBPARTITION(subpartition_name [,subpartition_name...])
analyze_statistics_clause:
COMPUTE STATISTICS [analyze_for_clause]
| ESTIMATE STATISTICS [analyze_for_clause] [SAMPLE INTNUM {ROWS | PERCENTAGE}]
analyze_for_clause:
FOR TABLE
| FOR ALL [INDEXED | HIDDEN] COLUMNS [size_clause]
| FOR COLUMNS [size_clause] column [size_clause] [,column [size_clause]...]
size_clause:
SIZE integer
| SIZE REPEAT
| SIZE AUTO
| SIZE SKEWONLY
column:
column_name
| (column_name [,column_name])
上述语法中,指定列的统计信息直方图收集方式和 DBMS_STATS 包的 method_opt 语法相同,请参见 method_opt。
与 DBMS_STATS 包收集统计信息的方式相比,ANALYZE 语句并没有提供那么丰富的策略设置。为了支持 ANALYZE 语句只收集 GLOBAL 级别的统计信息,约定规则是只需要在 use_partition 语法中设置 partition_name 为表名即可。
Oracle 模式下 ANALYZE 语句统计信息收集示例如下:
收集用户
user的表tbl1的统计信息。ANALYZE TABLE tbl1 COMPUTE STATISTICS FOR ALL COLUMNS SIZE AUTO;收集用户
user的表t_part1的GLOBAL级别的统计信息,并且只收集数据分布不均匀的列的直方图。ANALYZE TABLE t_part1 PARTITION (t_part1) COMPUTE STATISTICS FOR ALL COLUMNS SIZE SKEWONLY;收集用户
user的表t_subpart1的分区p0sp0和p1sp2的统计信息,所有的列的直方图都由优化器内部决定。ANALYZE TABLE t_subpart1 SUBPARTITION(p0sp0,p1sp2) COMPUTE STATISTICS FOR ALL COLUMNS SIZE AUTO;
手动统计信息收集的实践场景
非分区表的统计信息收集
当表的数据量和列的个数的乘积不高于一千万时,推荐使用如下命令收集非分区表的统计信息。如下示例中表 test.t1 有 10 个列,同时数据量在一百万行。
CREATE TABLE test.t1(c1 INT, c2 INT, c3 INT, c4 INT, c5 INT, c6 INT, c7 INT, c8 INT, c9 INT, c10 INT);
CREATE TABLE t2 (c1 INT PRIMARY KEY);
INSERT INTO t2 VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT /*+APPEND*/ INTO t1 SELECT * FROM (SELECT s0.c1 c1, s1.c1 c2, s2.c1 c3, s3.c1 c4, s4.c1 c5, s5.c1 c6, s0.c1 * 10 c7, s1.c1 * 10 c8, s2.c1 * 10 c9, s3.c1 * 10 c10 FROM t2 s0, t2 s1, t2 s2, t2 s3, t2 s4, t2 s5);
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1');
当表的数据量和列的个数的乘积超过一千万时,推荐根据数据业务情况和系统资源设置一定并行度来加快统计信息的收集。如下示例中表 test.t1 中的数据量增大到一千万行,使用 8 个并行度。
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1', degree=>8, method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1', degree=>8);
分区表的统计信息收集
相比较于非分区表,分区表的统计信息收集策略需要考虑分区统计信息收集。在系统资源允许的情况下,对于分区表的统计信息收集策略,推荐将收集非分区表的并行度再额外增加一倍。
如下示例中,相同场景的 test.t_part 表有 10 个列,同时数据量在一百万行。由于增加了分区统计信息的收集,因此并行度增加为 2。
CREATE TABLE t_part(c1 INT, c2 INT, c3 INT, c4 INT, c5 INT, c6 INT, c7 INT, c8 INT, c9 INT, c10 INT) PARTITION BY HASH(c1) PARTITIONS 128;
CREATE TABLE t2 (c1 INT PRIMARY KEY);
INSERT INTO t2 VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT /*+APPEND*/ INTO t_part SELECT * FROM (SELECT s0.c1 c1, s1.c1 c2, s2.c1 c3, s3.c1 c4, s4.c1 c5, s5.c1 c6, s0.c1 * 10 c7, s1.c1 * 10 c8, s2.c1 * 10 c9, s3.c1 * 10 c10 FROM t2 s0, t2 s1, t2 s2, t2 s3, t2 s4, t2 s5);
针对分区表,除了上述增加并行度以外,也可以考虑分区推导方式的收集,即收集分区的统计信息,进而通过分区统计信息推导全局统计信息,以加快收集的效率。例如同样的场景,不增加并行度,而是调整收集的分区方式,示例如下。
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't_part', granularity=>'APPROX_GLOBAL AND PARTITION', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't_part', granularity=>'APPROX_GLOBAL AND PARTITION');
更多关于分区表统计信息的收集与更新策略,以及详细示例 参见分区表统计信息的收集与更新策略。
Schema(数据库)级别的统计信息收集
除了手动的对单表的统计信息收集以外,OceanBase 数据库 DBMS_STATS 系统包还提供了对整个用户下的所有表进行统计信息收集。
说明
该功能仅在 OceanBase 数据库 V4.x 版本的双模式和 OceanBase 数据库 V3.2.x 版本的 Oracle 模式下支持。
由于收集某个用户下的所有表统计信息比较耗时,因此,建议在业务低峰期使用该功能。
如果该用户下所有表的数据量都是一些小表(数据量不超过一百万行),可以直接使用如下示例中收集 test 用户的统计信息命令。
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test');
当收集的用户下存在一些大表(行数在千万级别)时,可以在业务低峰期增大并行度来收集:
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test', degree=>'16', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test', degree=>'16');
如果用户下存在超大表(行数超过 1 亿)时,可以选择针对超大表开大并行单独收集,然后锁定超大表的统计信息再使用上述命令收集整个用户的,收集完成后在解锁超大表的统计信息,后续按照增量模式收集,示例如下:
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 'big_table', degree=>128, method_opt=>'for all columns size 1');
CALL DBMS_STATS.LOCK_TABLE_STATS('test','big_table');
CALL DBMS_STATS.GATHER_SCHEMA_STATS('TEST', degree=>'16', method_opt=>'for all columns size 1');
CALL DBMS_STATS.UNLOCK_TABLE_STATS('test','big_table');
在每次收集后,务必执行健康检查:
-- 示例:验证 test.t1 是否收集成功
SELECT table_name, num_rows, last_analyzed
FROM oceanbase.DBA_TAB_STATISTICS
WHERE owner = 'test' AND table_name = 'T1' AND object_type = 'TABLE';
统计信息收集进度与健康状态监控
为什么需要监控?统计信息收集可能耗时数分钟至数小时。您需要:
- 实时了解任务是否仍在运行
- 判断是否卡住或失败
- 验证收集结果是否有效
- 评估统计信息是否“过期”或“失真”
查询统计信息收集进度
OceanBase 数据库从 V4.2.0 版本开始支持统计信息收集监控诊断功能。手动或自动发起统计信息收集后,可以通过 (G)V$OB_OPT_STAT_GATHER_MONITOR 视图查询实时收集状态与进度。
有关该视图的字段含义、示例与更多诊断方法,参见 统计信息收集监控诊断。
查看当前正在运行的收集任务
说明
- MySQL 模式下通常使用
oceanbase.GV$OB_OPT_STAT_GATHER_MONITOR(或oceanbase.V$OB_OPT_STAT_GATHER_MONITOR)。 - Oracle 模式下通常使用
SYS.GV$OB_OPT_STAT_GATHER_MONITOR(或SYS.V$OB_OPT_STAT_GATHER_MONITOR)。
SELECT
tenant_id,
svr_ip,
svr_port,
session_id,
trace_id,
task_id,
type,
task_start_time,
task_duration_time,
task_table_count,
completed_table_count,
running_table_owner,
running_table_name,
running_table_duration_time,
running_table_progress
FROM oceanbase.GV$OB_OPT_STAT_GATHER_MONITOR
WHERE type = 'MANUAL GATHER';
| 字段 | 说明 |
|---|---|
TASK_TABLE_COUNT |
该任务需要收集的表数量 |
COMPLETED_TABLE_COUNT |
该任务已完成的表数量 |
RUNNING_TABLE_OWNER / RUNNING_TABLE_NAME |
当前正在收集的表 |
RUNNING_TABLE_PROGRESS |
当前正在收集表的进度(字符串形式展示) |
如果希望查看“当前租户在当前 OBServer 节点上”的实时状态,请使用 V$OB_OPT_STAT_GATHER_MONITOR。
查看历史任务记录(任务级)
-- 查询手动收集任务的历史执行情况
SELECT
tenant_id,
task_id,
type,
status,
table_count,
failed_count,
start_time,
end_time
FROM oceanbase.DBA_OB_TASK_OPT_STAT_GATHER_HISTORY
WHERE type = 'MANUAL GATHER'
ORDER BY start_time DESC;
查看历史任务记录(表级)
-- 查询手动收集过程中失败的表(若无失败则无返回)
SELECT
owner,
table_name,
task_id,
status,
start_time,
end_time,
stat_refresh_failed_list,
properties
FROM oceanbase.DBA_OB_TABLE_OPT_STAT_GATHER_HISTORY
WHERE status = 'FAILED'
ORDER BY start_time DESC;
STAT_REFRESH_FAILED_LIST表示“统计信息已收集完成,但刷新统计信息缓存失败”的节点列表。出现该字段时,建议结合视图信息排查相关节点状态,并在必要时刷新 Plan Cache。
验证统计信息是否生效或健康
收集完成后,需确认统计信息已写入并处于可用状态。
检查表级统计信息是否存在
说明
- MySQL 模式下可使用
oceanbase.DBA_TAB_STATISTICS、oceanbase.DBA_TAB_COL_STATISTICS等视图。 - Oracle 模式下可使用
SYS.DBA_TAB_STATISTICS、SYS.DBA_TAB_COL_STATISTICS等视图。
SELECT
table_name,
num_rows, -- 行数
avg_row_len, -- 平均行长度
sample_size, -- 采样大小
last_analyzed, -- 最后收集时间
stale_stats, -- 是否过期
stattype_locked -- 是否锁定
FROM oceanbase.DBA_TAB_STATISTICS
WHERE owner = 'test' AND table_name = 'T1' AND object_type = 'TABLE';
健康标志:
last_analyzed为近期时间(与本次收集时间一致)num_rows/sample_size等关键字段为非空且与业务规模大致匹配stale_stats = 'NO'(统计信息未过期)
检查列直方图是否生成(若指定了 SIZE > 1)
SELECT
column_name,
num_distinct, -- 不同值数量
num_nulls, -- NULL 值数量
num_buckets, -- 桶个数
histogram, -- 直方图类型
last_analyzed
FROM oceanbase.DBA_TAB_COL_STATISTICS
WHERE owner = 'test' AND table_name = 'T1';
直方图健康判断:
histogram字段显示直方图类型(例如HYBRID),且num_buckets > 1时通常表示已生成直方图。num_distinct/num_nulls等统计值建议与业务预期做一致性校验(避免采样过小或策略不当导致失真)。
检查统计信息是否被锁定(防止自动更新覆盖)
SELECT stattype_locked
FROM oceanbase.DBA_TAB_STATISTICS
WHERE owner = 'test' AND table_name = 'BIG_TABLE' AND object_type = 'TABLE';
stattype_locked = 'ALL':已锁定NULL:未锁定
常见问题诊断
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 收集任务长时间无进展 | 表极大、并行度低、资源争抢 | 增加 degree,在业务低峰期执行 |
num_rows = 0 或 NULL |
收集未完成或失败 | 检查 DBA_OB_TASK_OPT_STAT_GATHER_HISTORY、DBA_OB_TABLE_OPT_STAT_GATHER_HISTORY |
| 直方图未生成 | method_opt 未指定 SIZE > 1 |
显式设置 FOR COLUMNS col SIZE 254 |
| 查询仍走错误计划 | 统计信息未刷新 Plan Cache | 执行 ALTER SYSTEM FLUSH PLAN CACHE; |