WEEK 06 0 / 7 天
两年之后:索引与执行计划
时间快进:公司跑了一年多,订单冲到一百万,日报从一开始的秒出变成一分多钟。老板的原话:「它是不是坏了?」你的任务:让它快回去。
目标:看得懂 EXPLAIN ANALYZE,能说清一条查询为什么慢、加什么索引能救。
剧情 「时间快进」日:把库冲到 100 万订单 / 250 万明细 / 80 万支付 / 50 万登录。灌完你会发现,上周还秒出的查询现在慢得离谱--先建第一个索引救急,顺便搞懂它为什么快。
学 · 40 min
练 · 55 min
- 跑 seed.sql「W6」段大灌数,各表
count(*)核对行数 - 记录 3 条常用查询的基线耗时(
\timing,比如按用户查订单) - 对
orders.user_id无索引查询计时,建索引后再计时,记录倍数 - 用
pg_relation_size对比表和索引各自占多大 - 在有索引和无索引的表上各批量插 10 万行,对比写入耗时
过关说出加索引前后的倍数;能主动讲出 PG 与 MySQL 在表组织方式上的差异--这是很好的加分点。
剧情 不同的慢法用不同的索引:状态列只有几个值、搜索要忽略大小写、商品名要做全文搜。一个 B-tree 打不了天下。
学 · 45 min
练 · 60 min
- 给
status = 'pending'建部分索引,对比它和全量索引的大小 - 建
lower(email)表达式索引,验证where lower(email)=...能命中 - 加一个 jsonb 列,建 GIN 索引并做包含查询
- 在时间列上分别建 B-tree 和 BRIN,对比索引大小
- 用
tsvector+ GIN 做一次商品名全文检索
过关能各举一个部分索引和表达式索引的真实用途。
剧情 报表查询都带着「某用户的某时间段」--单个索引救不了组合条件。
学 · 45 min
练 · 60 min
- 建
(user_id, created_at)复合索引,测三种查询(只用 user_id / 只用 created_at / 两者都用)是否命中 - 把列顺序反过来再建一次,重测上面三种查询
- 只 SELECT 索引里的列,观察计划里出现
Index Only Scan - 用
INCLUDE把 total_amount 加进索引,验证仍是 Index Only Scan - 查
pg_stats看几个列的n_distinct,判断选择性高低
过关给定三条查询,能设计出「最少数量」的索引集合并说明理由。
剧情 老板问「到底慢在哪」。你不能再说「感觉是索引问题」--要拿执行计划说话。
学 · 50 min
练 · 55 min
- 挑 5 条查询做
EXPLAIN (ANALYZE, BUFFERS),逐行写注释 - 找出一条估算 rows 与实际相差 10 倍以上的查询,分析原因
- 找一个 Nested Loop 节点,用 loops 算出它的真实总耗时
- 对同一条 SQL 跑 EXPLAIN 和 EXPLAIN ANALYZE,说出区别
- 用
FORMAT JSON输出一次,观察结构
过关拿到一份陌生执行计划,能在 2 分钟内指出瓶颈在哪个节点、依据是什么。
剧情 同一条查询,优化器有时走索引有时全表扫,有时 Hash Join 有时 Nested Loop。你要能看懂它的选择逻辑。
学 · 45 min
练 · 60 min
- 逐步放宽 WHERE 的选择率,观察计划从 Index Scan -> Bitmap -> Seq Scan 的切换点
- 用
SET enable_hashjoin = off强制换算法,对比耗时 - 小表连大表,观察优化器选谁做驱动表(hash 表建在哪边)
- 把
work_mem调到 64kB,观察 Hash Join 落盘(计划里出现 Batches > 1) - 整理一张「三种 join 算法 × 适用条件 × 复杂度」对比表
过关能说出三种 join 算法各自的复杂度和前提条件(如 Merge Join 需要有序输入)。
剧情 索引建了,查询还是慢?你逐个复现六种「索引装死」的写法,还发现统计信息过期会让优化器选错路。
学 · 40 min
练 · 65 min
- 逐个复现 6 种失效场景,每种都保存失效前后的执行计划
- 把
date(created_at) = '2026-01-01'改写成范围查询,验证索引恢复命中 - 用 varchar 列与整数比较,观察隐式转换导致的全表扫
- 大批量更新后先查计划,再手动
ANALYZE,对比计划变化 - 装
pg_stat_statements,找出耗时 TOP10 的语句
过关产出一张「失效写法 -> 正确写法」对照表,至少 6 行,每行附执行计划证据。
剧情 交付日:从 pg_stat_statements 里挑出最慢的三条,把它们救活,写成可以进简历的案例。
学 · 10 min
练 · 85 min
- 从 pg_stat_statements 挑出 3 条秒级查询作为目标
- 逐条记录优化前的 EXPLAIN ANALYZE 和耗时
- 提出假设 -> 加索引或改写 SQL -> 验证
- 记录优化后的计划与耗时,算出提升倍数
- 整理成对比表格:查询 | 优化前 | 优化后 | 手段 | 提升倍数
过关三条查询都有明确提升倍数,且能解释每一条为什么快了。这是简历上可以直接写的东西。