AustinDatabases

捋一捋到底SQL慢了是哪里的原因,说SQL需要优化的可以退下了

❝

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群加群请联系 liuaustin3 ,(共3400人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 )(1 2 3 4 5 6 7 8群已经爆满  9群 为纯聊天群,默认不加入不得发广告,自己公众号文章链接等,发一次直接踢,默认加入8群,开10群PolarDB专业学习群115+)

今天我们将开启一个新的系列,我想探究一些数据库更深层次运行的原理。

摘要 当一条 SQL 突然慢下来,绝大多数人的第一反应是「SQL 写得不好」。但真正棘手的问题往往不是 SQL 本身,而是数据库内核在统计信息、基数估算、JOIN 顺序、执行计划、MVCC、存储引擎这一整条链路上某一步「猜错了」。

Image

1「数据库为什么会变慢?我们用一个故事开始: 某天凌晨,监控告警炸了。一条跑了三年的报表 SQL,平时只要 200ms,突然变成了 45 秒。

开发同学第一反应是:「SQL 我又没动过,数据量也才涨了 5%,怎么就慢成这样?」

排查链路非常典型:

索引还在?✅ 执行计划变了?❌(变成 Nested Loop 了,原来是 Hash Join)

统计信息是新的?❌(ANALYZE 还没跑)

为什么计划变了?❌(基数估算从「100 万」变成了「100」)

看到这里你大概已经明白:问题不在 SQL,而在优化器「猜错了基数」。一条 SQL 的快与慢,从你按下回车那一刻起,就被一连串内核决策决定了。这条决策链,就是本文要拆解的对象。

SQL 文本

↓

解析 / 重写

↓

统计信息

↓

基数估算 (Cardinality Estimation)

↓

成本模型 (Cost Model)

↓

JOIN 顺序 / JOIN 算法

↓

执行计划

↓

执行器

↓

存储引擎 (Heap / B-tree)

↓ MVCC

↓

IO / Buffer

02 统计信息:优化器的「天气预报」 PostgreSQL 优化器在生成执行计划时,并不真正去扫描数据——它没法承担那个代价。它依赖的是 pg_statistic 里的一堆「统计摘要」:

MVCC 表的 行数估计(reltuples)

直方图(most_common_vals / histogram_bounds)

distinct 值数量(ndistinct)

列的相关性(correlation,影响索引扫描代价估算)

-- 看一张表目前的统计快照
SELECT relname, relpages, reltuples
FROM pg_class
WHERE relname = 'orders';

-- 看某列的直方图与 MCV
SELECT attname, n_distinct,
       most_common_vals, most_common_freqs,
       histogram_bounds
FROM pg_stats
WHERE tablename = 'orders' AND attname = 'status';

关键点:统计信息是一个「采样摘要」,它永远不等于真实数据分布。当数据分布倾斜、或统计信息过期,优化器就拿着一份错的「地图」去做决策。

注意 default_statistics_target 默认是 100,意味着直方图只有 100 个桶。如果某列数据高度倾斜(例如订单状态列 99% 是 'PAID'),100 个桶可能根本拟合不出真实分布。

统计信息为什么会失真?

大批量 UPDATE / INSERT 后还没 ANALYZE autovacuum 触发条件没达到(默认 analyze_scale_factor = 0.1,10% 才会自动分析);

数据倾斜严重,默认采样桶数不够;

跨列相关性无法表达(ndistinct 只统计单列,多列组合时优化器只能假设独立);

函数表达式结果分布没有统计(PG 14 之后的 extended statistics 才部分解决)。

统计信息一旦失真,下一步——基数估算——就开始出错。

基数估算:数据库最危险的一步 基数估算(Cardinality Estimation)回答的是:"这个过滤条件之后,还剩多少行?"

SELECT *
FROM orders
WHERE status = 'PAID'
  AND created_at >= '2026-01-01';

优化器需要猜:status = 'PAID' 剩多少行?created_at >= '2026-01-01' 剩多少行?两个条件组合之后剩多少行?(假设独立 → 相乘)这一步是整个链路最危险的,因为后面所有的决策都建立在它之上.

错误基数估算

↓

错误成本估算

↓

错误 JOIN 顺序

↓

错误 JOIN 算法

↓

错误执行计划

↓

SQL 突然变慢

一个典型翻车场景:

实际:100 万行

估算:100 行

优化器选了 Nested Loop + Index Scan(适合小结果集)实际跑出来:100 万次索引回表,磁盘随机 IO 爆炸核心问题 基数估算错一点,后面全部错。这不是"SQL 写得不好",而是优化器拿着一张错地图导航。

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders
WHERE status = 'PAID'
  AND created_at >= '2026-01-01';
  --如果 rows=100 而 actual rows=1000000,那就是基数估算翻车了。

PostgreSQL 的基数估算逻辑分布在 costsize.c 里,主要函数是 set_baserel_size_estimates() 和 estimate_selectivity()。它本质上是一套基于统计摘要和独立性假设的公式。

多列相关的查询(例如 WHERE country = 'CN' AND city = 'Beijing')会让"独立性假设"彻底失效——这是 PG 14 引入 CREATE STATISTICS 扩展统计的根本原因。

04 JOIN 顺序:为什么 10 张表 JOIN 还行,30 张表就崩了?

当基数估算出来的"每张表剩下多少行"交给优化器之后,下一个问题是:N 张表 JOIN,应该按什么顺序连?

这个问题看似无害,实则是 PostgreSQL 优化器最复杂的部分之一。N 张表 JOIN,理论上有 N! 种排列方式(如果不考虑 JOIN 树形状)。这个增长速度极其恐怖——它是阶乘级,比指数还猛

Image

动态规划:PostgreSQL 的默认策略当表数量较少时,PG 用动态规划(DP)枚举所有可能的 JOIN 顺序,并为每种组合算一个代价,最后挑最便宜的。

DP 的核心思想是:

把"找 N 张表最优 JOIN 顺序"拆成"找每张子集的最优顺序";子问题的解可以复用;复杂度大约是 O(3^N)(实际还要看 JOIN 图形状)。 动态规划的好处是精确——它一定能找到"代价模型下"的最优计划。坏处是当 N 一大,复杂度爆炸。当 JOIN 的表数超过一个阈值,PostgreSQL 会自动切换到 GEQCO(Genetic Query Optimization)——一种基于遗传算法的启发式搜索。

这个阈值由参数控制:

geqo_threshold = 12   -- 默认值:JOIN 表数 ≥ 12 时切到 GEQO

GEQO 不再穷举,而是用遗传算法在搜索空间里"随机游走 + 优胜劣汰",找到一个"还不错"的 JOIN 顺序就交差。GEQO 的代价 它不保证最优。也就是说,同样一条 SQL,两次执行计划可能不同。这是为什么很多 DBA 在生产环境直接把 geqo 关掉或者调高 geqo_threshold。

建一个有 20~30 张表的 JOIN,对比 geqo=on vs geqo=off,可以明显看到: 计划生成时间差异巨大(DP 慢但精确,GEQO 快但不稳定) 实际执行时间可能相差 10 倍以上 两次 EXPLAIN 输出可能不同(GEQO 是随机的)

那么GEQO怎么调,GEQO是什么,怎么用,咱们下期继续,

PostgreSQL 版本升级方法总结,具体pg_upgrade怎么操作

与OceanBase集中式摸爬滚打的4个月,我得到了什么 ?
醋评 数据库行业 “不行了”  ---来自五彩斑斓乌鸦的 3336个字
《没有人为不需要的性能付费 经济下行,正在倒逼数据库"做减法"》

PostgerSQL 14-17备份的变化 PG17更贴近商业数据库 与 实际命令

PostgreSQL 怎么用好高版本的PG调优--PG14-PG18

同学问 PG17 的备份比老的版本 好哪了? 你给总结总结 !!

算法领主与数据农奴:AI时代的不能说的问题-- 此文为AI临时工所做与公众号作者无关

《AI为什么迟迟进不了企业核心系统?我总结了八个原因》

《AI不是出事了,而是我们开始看到它的代价》
NOSQL 怎么翻盘,降本增效为企业节省资源,--DTCC 通过NOSQL给企业系统瘦身

怎么AI设定评估成本模型思考

MySQL 写不进去数据,程序报错,谁的问题?

从亚马逊 AGI 部门裁员看 AI 商业逻辑的必然转向  -- 资本不会给AGI 半点脸

比起简单的Skill技能,我更想建立Agent Skill的系统思维能力--- 感谢本书作者答疑解惑

  MongoDB 全文索引 与 展示查询数据的一部分,提高性能

体现价值-我们靠PostgreSQL迁移PolarDB,给公司省下了100万 “巨款”

《告别迁移焦虑:OceanBase MySQL 模式能否兼容 DBA 的“祖传”运维 SQL?》

干数据库不是买白菜:光盯着License几毛钱,看不见300台机器的电费?

一个秘密,不是你 SQL 写对了,是优化器帮“擦了屁股”  客户问迁移后为什么快了--迁移到PolarDB后的故事

AI 时代,我却用不上一个靠谱的数据库产品

AI 引入后,MySQL 列权限控制,插入,更新,读取,删除 --有了AI 真是越帮越忙

PostgreSQL 大表改字段卡死的问题解决了吗?  解决了方案在此

AI 引入DBA 工作,造成工作量增加,忙不过来,根本忙不过来!!!

三无项目导致MongoDB 持续1406% CPU 问题解决

image