评审数据库索引设计
按真实查询逐条核对索引,指出冗余、失效和写放大
你是数据库性能评审者。根据下面的表结构、数据量和慢查询,逐条评审索引:1)每个索引对应哪条查询,写出走索引的 WHERE/ORDER BY 片段;2)标出冗余索引(最左前缀已被覆盖)、永远用不上的索引,以及会失效的写法(函数、隐式转换、前导通配符);3)估算新增索引对写入和存储的影响;4)给出「保留/删除/新增」三栏结论。不要只按字段名猜查询。 [表结构/索引/慢查询/数据量级]
使用建议
先问「这条索引服务哪条 SQL」;答不上来的索引,大概率只是在占空间和拖慢写入。
适合什么场景
- 几条慢查询在榜单上挂了几周,陆陆续续加索引却没有改善。
- 准备给写多读少的表加索引,需要先确认现有索引是否已经覆盖。
不适合的场景
- 表只有几千行、能整个放进内存:调索引不会改变结果。
- 写入吞吐已经是瓶颈:再加索引只会更慢,不会更快。
示例
输入
CREATE TABLE orders (... user_id BIGINT, status VARCHAR(16), created_at DATETIME, ...); INDEX idx_user (user_id), INDEX idx_user_status (user_id,status), INDEX idx_status (status); SELECT * FROM orders WHERE user_id=? AND status='paid' ORDER BY created_at DESC LIMIT 20;
你会得到
`idx_user` 被 `idx_user_status` 的最左前缀完全覆盖,可以删;但查询还要按 `created_at` 排序,`idx_user_status` 仍会 filesort,应评估 `(user_id,status,created_at)`。`idx_status` 的区分度取决于状态取值;如果只有几种状态,它只有在按状态批量扫描时才有价值,删除前先确认它是否服务其他 SQL。结论要落在「保留/删除/新增」三栏,并说明每新增一个索引要付的写入和存储成本。
常见误用
- 把 `possible_keys` 里出现当成走了索引:`key` 为 NULL 或扫描行数接近全表时,索引等于没用。
- 看到慢就加索引:写多读少的表上,冗余索引会让每次写入多维护几棵树。
可以这样改
- 加「给出索引占用的磁盘空间和写入延迟估算」,评审会从「有没有用」推进到「值不值」。
- 要求「先列查询再列索引」,能避免按字段名拼索引的常见错误。