火柴人武林大会
156.74M · 2026-02-04
一睁眼,线上的数据库CPU直接飙到了100%!
就在刚刚,因为一位“热心”的高级开发给订单表加了3个看似完美的索引,导致整个系统的写入速度瞬间腰斩,不仅没救活系统,反而引发了连锁崩塌。
正如PostgreSQL社区大佬Robert Haas的那句名言:“索引不是免费午餐,它是一笔昂贵的技术债务。”
很多兄弟有个误区:觉得系统慢了,只要无脑 CREATE INDEX 就能药到病除。
大错特错。
说大白话,索引本质上就是一本“字典目录”。你查字是快了,但你想过没有?每次往字典里加一个新字,你不仅要写正文,还得把目录重新排版一遍。
这就是“空间换时间”的代价。
数据不会撒谎。在没有任何索引的情况下,插入1000条数据可能只需要几毫秒;但如果你给这张表加了10个索引,写入耗时可能会暴涨10倍以上。
问题来了:你现在的项目中,单表索引最多的有几个?超过5个的,建议你往下看,背心可能会出汗。
当然,我们不能因噎废食。在PostgreSQL的实战逻辑里,这四个场景属于“绿灯区”,请把索引焊死在这些字段上:
WHERE 子句里的常客。这是最基本的常识。JOIN 操作时的 ON 字段(外键)。不加这个,多表关联能慢到让你怀疑人生。ORDER BY 或 GROUP BY 的字段。什么概念?
利用索引的有序性,PostgreSQL 可以直接避免极其消耗内存的 FileSort 操作。在一个千万级数据的表中,这能把查询从 30秒缩短到0.1秒。
打开你的 EXPLAIN 分析,如果你看到 Seq Scan(全表扫描)变成了 Index Scan(索引扫描),恭喜你,这波操作稳了。
接下来的内容,可能会颠覆很多人的认知。这是本文的重点,也是无数事故的源头。
1. 表数据量极小 几百行数据的配置表,你建什么索引? PostgreSQL 的优化器极其聪明,对于这种小表,它直接把整个表加载到内存扫描,速度比去翻索引树快得多。加了索引反而多了回表的开销,纯属脱裤子放屁。
2. 数据区分度低(Cardinality) 这是新手最容易踩的坑。给“性别”、“状态(0/1)”这种字段建索引。 这种索引在生产环境几乎是废的。 当一个字段只有很少的取值时,优化器会认为:“反正都要扫半张表,不如直接全表扫算了。” 结果就是你建了索引,PG 压根不用。
3. 频繁更新的列 还记得开头那个CPU飙升的案例吗? 那就是在“订单状态”这个高频更新的字段上建了索引。大促期间,状态从“待支付”变“已支付”再变“发货”,每一次变化都要重建索引。在高并发下,这直接导致了严重的锁竞争(Lock Contention)。
4. 大文本/长字符串
给 Description 这种长文本建索引?你的磁盘空间是风刮来的吗?
这种场景,老老实实去用前缀索引,或者上全文检索(Full Text Search)。
最气人的不是没建索引,而是建了索引,但被你的垃圾 SQL 代码搞失效了。
避开这几个“隐形杀手”,别让你的努力付诸东流:
1. 对字段进行函数运算
WHERE year(create_time) = 2024WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01'原理很简单:你在字段上套了函数,数据库就必须把所有行都算一遍,索引直接作废。
2. 隐式类型转换
WHERE phone_number = 13800000000 (字段是 varchar)WHERE phone_number = '13800000000'不加引号,数据库会偷偷做类型转换,这又是一次全表扫描。
3. 模糊查询玩脱了
LIKE '%abc'LIKE 'abc%'记住:左模糊(%放在前面)是索引的死穴。
索引优化的核心从来不是“技术”,而是“权衡”。
读多写少,上索引;表小区分度低,别折腾。
下周上班,建议大家做一件事:
连上你的生产库,执行一下这条命令(一定要在低峰期):
SELECT * FROM pg_stat_user_indexes
WHERE idx_scan = 0;
把那些 idx_scan(索引扫描次数)为 0 或者极低的索引找出来。这些就是你数据库里的“吸血鬼”,请在评估后,大胆地把它们删掉。
数据库性能优化这条路,做减法往往比做加法更重要。
最后留个作业:你在过往的开发生涯中,遇到过最离谱的“慢查询”是什么原因造成的? 欢迎在评论区里晒出来,让大家避避坑!