统计信息机制对比:Oracle vs PostgreSQL vs MySQL
2026/9/1 1:06:57 网站建设 项目流程

优化器选错执行计划,90% 是统计信息的锅。三种数据库都提供统计信息,但直方图类型、多列统计、自动采集策略完全不同。本文从「优化器的眼睛」角度,拆解三库统计信息机制的差异。

前言:优化器为什么需要「看」数据

CBO 的核心是代价估算,而代价估算的前提是知道数据长什么样。举个例子:

SELECT*FROMusersWHEREgender='M';

如果gender列 95% 是 ‘M’,全表扫描可能是正确的;如果只有 1% 是 ‘M’,那走索引才合理。优化器只能通过统计信息来做出这个判断。

统计信息回答的核心问题有三个:

  1. 有多少数据?——表的行数、块数
  2. 数据怎么分布?——列的唯一值数、分布直方图
  3. 列之间有什么关联?——多列联合分布、函数依赖

这三种数据库都实现了统计信息,但精度和深度完全不同。今天就逐一拆解。

一、Oracle:统计信息体系的「豪华配置」

Oracle 的统计信息收集能力在三大数据库中最为成熟,几乎是「什么都能统计」。

1.1 基础统计信息

Oracle 通过DBMS_STATS包收集以下基础信息:

-- 收集表的统计信息BEGINDBMS_STATS.GATHER_TABLE_STATS(ownname=>'HR',tabname=>'EMPLOYEES',estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE,method_opt=>'FOR ALL COLUMNS SIZE AUTO',cascade=>TRUE-- 同时收集索引统计);END;/

基础统计信息存储在数据字典中,可以通过以下视图查询:

视图内容
DBA_TABLES/ALL_TABLES表级(行数、块数、平均行长)
DBA_TAB_COLUMNS列级(NDV、空值数、密度、直方图类型)
DBA_TAB_HISTOGRAMS直方图桶详情
DBA_INDEXES索引级(B+Tree 层级、叶块数、聚簇因子)
DBA_TAB_STATISTICS统一视图,展示所有统计信息的收集时间

其中聚簇因子(Clustering Factor, CF)是 Oracle 特有的概念——它衡量索引顺序与表中物理行顺序的匹配程度。CF 越接近表的块数,说明数据越「有序」,索引范围扫描效率越高。如果 CF 接近表的总行数,说明数据和索引完全「无序」——这是优化器偏好全表扫描的一个重要信号。

-- 查看索引的聚簇因子(越低越好)SELECTindex_name,clustering_factor,num_rowsFROMdba_indexesWHEREtable_name='EMPLOYEES';

1.2 直方图:让优化器「看见」数据倾斜

如果列数据是均匀分布的,只需要NUM_DISTINCT(NDV)就能准确估算基数。但现实中的数据几乎都有倾斜——比如 80% 的订单来自 20% 的客户。这时候就需要直方图。

Oracle 支持 4 种直方图类型(12c+):

直方图类型适用场景桶数上限
FREQUENCYNDV ≤ 254,且每个值都能放进自己的桶254
TOP-FREQUENCY有少量高频值 + 大量低频值254
HEIGHT-BALANCED旧版(12c 前),不推荐使用254
HYBRID12c 默认,数据倾斜的通用场景254

其中HYBRID直方图(12c 默认)是 Oracle 的改进重点——它结合了频率直方图和等高直方图的优点。对于高频值,它像 FREQUENCY 一样精确记录;对于低频值,它按范围聚合,节省存储空间。

-- 查看某列的直方图信息SELECTcolumn_name,histogram,num_buckets,num_distinctFROMdba_tab_col_statisticsWHEREtable_name='EMPLOYEES'ANDcolumn_name='DEPARTMENT_ID';-- 查看直方图的桶详情SELECTendpoint_number,endpoint_value,endpoint_number-LAG(endpoint_number,1,0)OVER(ORDERBYendpoint_number)ASfrequencyFROMdba_tab_histogramsWHEREtable_name='EMPLOYEES'ANDcolumn_name='DEPARTMENT_ID'ORDERBYendpoint_number;

1.3 扩展统计:多列关联和表达式统计

单列直方图有一个致命缺陷:它假设列之间是独立的。比如citystate高度相关(知道了城市就知道了州),但优化器如果只看到两列各自的直方图,会把两列的过滤率简单相乘,导致基数估算严重偏低。

Oracle 的解决方案是扩展统计(Extended Statistics)

-- 1. 多列统计(Column Group)SELECTDBMS_STATS.CREATE_EXTENDED_STATS(ownname=>'HR',tabname=>'EMPLOYEES',extension=>'(CITY, STATE)'-- 列组)FROMdual;-- 2. 表达式统计(Expression Statistics)SELECTDBMS_STATS.CREATE_EXTENDED_STATS(ownname=>'HR',tabname=>'EMPLOYEES',extension=>'(UPPER(LAST_NAME))'-- 函数表达式)FROMdual;-- 查看已创建的扩展统计SELECTextension_name,extensionFROMdba_stat_extensionsWHEREtable_name='EMPLOYEES';

创建扩展统计后,Oracle CBO 在估算WHERE city='Beijing' AND state='BJ'时会使用列组的联合分布直方图,而不是把两个独立列的选择率相乘——这意味着更准确的基数估算。

1.4 动态采样与自动统计收集

Oracle 还有一个「兜底」机制——动态采样(Dynamic Sampling):

-- 查看动态采样级别(默认 2,即 OPTIMIZER_DYNAMIC_SAMPLING=2)SELECTname,valueFROMv$parameterWHEREname='optimizer_dynamic_sampling';

动态采样的逻辑是:如果优化器发现某个表没有统计信息(比如刚创建的表、临时表),在生成计划时会实时扫描少量数据块来估算统计信息。虽然有额外开销,但总比瞎猜强。

日常统计维护方面,Oracle 默认启用了自动统计收集任务,每天夜间窗口运行:

-- 查看自动统计收集任务的运行历史SELECTjob_name,last_start_date,statusFROMdba_scheduler_jobsWHEREjob_nameLIKE'%GATHER_STATS%'ORjob_nameLIKE'%STATS%';-- 查看哪些表的统计信息过期了(变化超过 10%)SELECTtable_name,num_rows,stale_statsFROMdba_tab_statisticsWHEREstale_stats='YES';

经验之谈:生产系统中,不要等到自动任务发现统计过期了才收集。核心业务表(日增百万行以上)建议在 ETL 流程结束后立即执行DBMS_STATS.GATHER_TABLE_STATS,避免白天高峰期触发自动统计收集导致性能抖动。

二、PostgreSQL:透明但够用的统计体系

PostgreSQL 的统计信息存储在一张关键系统表pg_statistic中,通过pg_stats视图对外暴露。

2.1 基础统计信息

-- 查看一张表的列统计信息SELECTattname,null_frac,avg_width,n_distinct,most_common_vals,most_common_freqs,histogram_bounds,correlationFROMpg_statsWHEREtablename='employees';

关键字段解读:

字段含义例子
null_fracNULL 值比例0.02 表示 2% 是 NULL
avg_width列平均宽度(字节)4 表示 INT 列
n_distinct唯一值数-1 表示唯一值数 = 总行数(唯一列)
most_common_vals最常见值列表(MCV){‘Sales’, ‘Engineering’}
most_common_freqs对应频率{0.40, 0.35}
histogram_bounds等深直方图边界{1000, 2500, 4000, 6000, 9000}
correlation物理顺序与索引顺序的相关性1.0(完全正序)~ -1.0(完全逆序)~ 0.0(随机)

PG 统计信息的一个重要特征是correlation值——它相当于 Oracle 的聚簇因子,但表达方式不同。correlation接近 1 或 -1 时,索引范围扫描效率高;接近 0 时,优化器更倾向位图扫描或全表扫描。

2.2 直方图与 MCV

PG 使用两种机制来统计列的数据分布:

  • MCV(Most Common Values):记录出现频率最高的值及精确频率。由default_statistics_target控制存储多少个最常见值(默认 100);
  • 等深直方图(Equi-Depth Histogram):将除去 NULL 和 MCV 之后的剩余值,按出现次数分成 N 个等深桶,只记录每桶的边界值。桶数也由default_statistics_target控制。
-- 查看默认的统计目标(桶数)SHOWdefault_statistics_target;-- 默认 100-- 针对特定列提高统计精度ALTERTABLEemployeesALTERCOLUMNdepartment_idSETSTATISTICS1000;

default_statistics_target是一个重要的调优参数。默认值 100 对于大多数 OLTP 系统够用,但对于数据倾斜严重的列(比如 1000 万行中最常见的值占了 800 万行),提高这个值可以让优化器更精确地估算低频值的选择率。

2.3 扩展统计:解决列关联问题

PG 10+ 引入了与 Oracle 扩展统计类似的能力:

-- 创建多列依赖统计CREATESTATISTICSs_city_state(dependencies)ONcity,stateFROMemployees;-- 创建多列 NDV 统计(用于 GROUP BY 多列场景)CREATESTATISTICSs_dept_title(ndistinct)ONdepartment_id,job_titleFROMemployees;-- 创建 MCV 扩展统计(用于多列联合过滤场景)CREATESTATISTICSs_multi(mcv)ONcity,state,ageFROMemployees;-- 查看已创建的扩展统计SELECTstxname,stxkind,stxddependencies,stxdndistinctFROMpg_statistic_ext;

三种扩展统计类型:

类型解决的问题示例场景
dependencies列之间的函数依赖city决定了state,选择率不应相乘
ndistinct多列组合的唯一值数GROUP BY a, b的组数估算
mcv多列联合分布WHERE city='X' AND state='Y'的组合频率

创建扩展统计后,需要重新收集统计信息才会生效:

ANALYZEemployees;

2.4 自动收集:autovacuum 的另一面

PostgreSQL 的自动统计收集由autovacuum子系统负责(它的角色不仅仅是清理死元组):

-- 查看 autovacuum 的统计收集配置SELECTname,settingFROMpg_settingsWHEREnameLIKE'autovacuum_analyze%';

关键参数:

参数默认值含义
autovacuum_analyze_threshold50触发 ANALYZE 的最小变化行数
autovacuum_analyze_scale_factor0.1触发 ANALYZE 的变化比例(10%)

规则:当修改行数 > threshold + 表总行数 × scale_factor时自动 ANALYZE。对于大表(1 亿行),需要变化 1000 万行才会触发——这可能在数据倾斜场景下不够及时。建议对频繁更新的核心大表手动调低autovacuum_analyze_scale_factor(比如 0.05)。

三、MySQL:从 5.7 到 8.0,统计信息的追赶之路

3.1 InnoDB 统计信息

MySQL 的统计信息由存储引擎层负责,InnoDB 的统计信息有两种模式:

-- 查看统计信息模式SHOWVARIABLESLIKE'innodb_stats%';
参数默认值含义
innodb_stats_persistentON (8.0+)持久化存储(重启不丢失)
innodb_stats_auto_recalcON当表变化 10% 时自动更新
innodb_stats_persistent_sample_pages20持久化统计的采样页数
innodb_stats_transient_sample_pages8非持久化统计的采样页数

持久化统计信息存储在mysql.innodb_table_statsmysql.innodb_index_stats表中:

-- 查看 InnoDB 持久化统计SELECT*FROMmysql.innodb_table_statsWHEREtable_name='employees';SELECT*FROMmysql.innodb_index_statsWHEREtable_name='employees';

一个重要陷阱innodb_stats_persistent_sample_pages默认只有 20 页。对于几十 GB 的大表,20 页采样可能严重失准——优化器可能会「认为表很小」而选择全表扫描。大表建议调整为 200-500 页。

3.2 MySQL 8.0 直方图:终于来了

MySQL 8.0 是第一个支持直方图的版本(此前只能靠索引统计来间接估算):

-- 为某列创建直方图ANALYZETABLEemployeesUPDATEHISTOGRAMONdepartment_idWITH100BUCKETS;-- 查看直方图信息SELECT*FROMinformation_schema.COLUMN_STATISTICSWHERESCHEMA_NAME='my_db'ANDTABLE_NAME='employees'ANDCOLUMN_NAME='department_id'\G

MySQL 支持两种直方图类型:

类型对应适用场景
SINGLETON频率直方图NDV ≤ 桶数,每个值一个桶
EQUI-HEIGHT等高直方图NDV > 桶数,按出现次数等深分桶
-- 查看直方图 JSON 内容(简化显示)SELECTJSON_PRETTY(HISTOGRAM)FROMinformation_schema.COLUMN_STATISTICSWHERECOLUMN_NAME='department_id'\G

直方图在创建后持久化到数据字典(重启不丢失),但统计信息不会自动更新。你需要定期手动执行ANALYZE TABLE ... UPDATE HISTOGRAM

3.3 MySQL 统计的限制

相比 Oracle 和 PG,MySQL 的统计信息体系仍有显著差距:

  1. 没有多列统计:无法处理列关联问题。WHERE city='X' AND state='Y'的基数估算仍然简单相乘;
  2. 没有表达式统计WHERE UPPER(name)='JOHN'这种场景优化器只能靠猜测;
  3. 采样精度受限于页数innodb_stats_persistent_sample_pages=20的默认值对 100GB+ 的表精度不足;
  4. 没有自动过期检测机制:统计是否过期需要 DBA 自己判断和管理。

四、三库统计信息对比

维度OraclePostgreSQLMySQL
直方图类型4 种(FREQUENCY/TOP-FREQ/HYBRID/H-BAL)MCV + 等深直方图SINGLETON + EQUI-HEIGHT(8.0+)
多列扩展统计列组 + 表达式统计dependencies + ndistinct + MCV(PG 10+)❌ 不支持
表达式统计✅ 支持❌ 不支持(靠表达式索引)❌ 不支持
自动收集机制自动任务 + 过期标记autovacuum ANALYZEinnodb_stats_auto_recalc(10% 阈值)
采样精度控制AUTO_SAMPLE_SIZE(自适应)default_statistics_targetinnodb_stats_*_sample_pages
统计持久化数据字典pg_statistic(系统表)mysql.innodb_*_stats
聚簇因子/相关性✅ Clustering Factor✅ correlation❌ 无此概念

五、生产环境统计信息最佳实践

Oracle

-- ETL 结束后立即收集(而非等自动任务)BEGINDBMS_STATS.GATHER_TABLE_STATS(ownname=>'HR',tabname=>'EMPLOYEES',estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE,method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY',degree=>4,cascade=>TRUE);END;/
  • AUTO_SAMPLE_SIZE:让 Oracle 自适应决定采样比例
  • SIZE SKEWONLY:只对倾斜列建直方图,节省资源
  • degree => 4:并行收集,适合大表

PostgreSQL

-- 核心表调高统计精度 + 降低自动分析阈值ALTERTABLEemployeesALTERCOLUMNdepartment_idSETSTATISTICS1000;ALTERTABLEemployeesSET(autovacuum_analyze_scale_factor=0.05);-- 创建多列扩展统计CREATESTATISTICSs_emp_multi(mcv)ONcity,state,department_idFROMemployees;ANALYZEemployees;

MySQL

-- 大表调高采样页数ALTERTABLEemployees STATS_PERSISTENT=1,STATS_SAMPLE_PAGES=200;-- 创建直方图并定期更新ANALYZETABLEemployeesUPDATEHISTOGRAMONdepartment_id,statusWITH100BUCKETS;

六、总结

三种数据库的统计信息体系差距明显:

  • Oracle:最成熟。四种直方图 + 扩展统计 + 表达式统计 + 自动采样 + 过期标记。是「豪华版」统计体系;
  • PostgreSQL:实用主义。MCV + 等深直方图 + 扩展统计(10+),暴露关键参数供 DBA 调节,清晰透明;
  • MySQL:正在追。8.0 加入直方图是重要里程碑,但缺少多列统计和表达式统计,仍有差距。

一句话:统计信息是优化器的眼睛。如果数据倾斜严重(大部分现实场景都这样),直方图是必须的。在多列关联很强的表上,Oracle 和 PG 的扩展统计能显著改善 JOIN 顺序选择——MySQL 目前还只能靠手工 SQL 改写来弥补。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询