问题排查总论:流程、工具与最佳实践
索引审计日志的巡检,说白了就是从海量日志里捞出有问题的盲索引。我自己做了多年数据库审计,发现很多团队卡在这一步——不是查不出来,是查出来不会定位。嗯,这章我们就聊聊排查的整套打法。
排查前的准备工作
动手之前,先把环境摸清楚。我个人习惯先确认三个基本盘:
- 日志源是否完整:审计日志有没有被截断?我曾经遇到过一次,ETL任务把旧日志覆盖了,导致漏掉一批慢查询。
- 索引元数据是否可读:
pg_indexes、sys.indexes这些系统表能否快速查询?如果权限不够,提前找DBA开白名单。 - 基线数据:正常场景下索引命中率是多少?没有基线,你就没法判断异常。
你想想看,没这些前置条件,排查就像在雾里开车。所以我建议在巡检脚本里先跑一遍环境检查。
排查的核心流程
排查盲索引,说白了就三步:抓日志 → 分析模式 → 验证结论。我不会把它搞成复杂方法论,直接上我常用的流程,配合下面的图你看一遍就懂了。
我的小技巧:每次排查我都用这流程走一遍,哪怕问题看起来很简单。习惯成自然,能避免漏掉深层原因。
常用排查工具清单
工具不在多,顺手就好。下面这张表是我这些年用下来最有效的几组组合,你可以直接复制到自己的工具箱里。
| 场景 | 推荐工具 | 一句话说明 |
|---|---|---|
| 日志采集 | pgBadger / mysqldumpslow | 快速从慢查询日志提取TOP SQL |
| 模式分析 | pt-query-digest / 自研SQL特征分类 | 按索引使用模式分组,一眼看出盲索引 |
| 索引诊断 | pg_stat_user_indexes / sys.dm_db_index_usage_stats | 直接查索引的扫描次数、命中率 |
| 深挖根因 | explain (analyze, buffers) / set statistics io on | 看执行计划+真实I/O,定位为什么不走索引 |
| 验证修复 | CREATE INDEX CONCURRENTLY / 在线DDL | 不锁表重建索引,生产环境必备 |
别小看 explain (analyze, buffers)。我曾经花了三天找一条慢SQL的盲索引,最后发现是统计信息太久没更新,计划器选了错误的索引。加上 buffers 参数后,脏页读一眼暴露。
最佳实践——我踩过的坑
避坑指南,都是真金白银换来的。我直接列几条最常见的,你对照看看有没有中招。
- 不要只看总命中率。有个索引命中率99.9%,但剩下0.1%是查全表的热点查询。我在项目中遇到过,那个索引其实是“伪优”,废了。
- 盲索引不一定是多余的。有时候索引本身没问题,是SQL写法导致无法使用——比如函数包裹索引列。这种情况别急着删索引,先改SQL。
- 警惕“一眼盲”陷阱。日志里出现大量全表扫描,不一定是索引问题,也可能是并发锁导致索引查询退化。我曾经被这个坑过,后来加了
pg_stat_activity的实时快照才抓到元凶。 - 验证阶段分两步:先在预发布环境跑,再慢放生产流量。安全第一,别直接删生产索引。
重要提醒:在生产环境执行 DROP INDEX 前,务必确认该索引没有被任何外键或唯一约束引用。我有个同事曾经误删了唯一约束的底层索引,导致数据插入失败,回滚花了两小时。
如何快速定位“真凶”
排查到最后,你会发现大多数盲索引都逃不过三种模式。我总结成了下面的快速定位清单,你可以当成检查表用。
- 情景A:索引未被使用 → 检查
seq_scan是否 >idx_scan,可能索引坏了或统计信息过期。 - 情景B:索引被使用但效率低 → 看
idx_tup_read / idx_tup_fetch比例,如果很多读取但返回行少,说明索引选择性差。 - 情景C:索引占用空间过大 → 对比
pg_relation_size和实际数据量,可能包含大量冗余字段。
为什么要把这几种情景分开?因为治疗手段完全不同。情景A需要 ANALYZE 或重建索引,情景B可能需要改成覆盖索引,情景C则要删掉不必要的包含列。
嗯,说到这就差不多了。记住一个原则:没有银弹,但一套清晰的流程能让你少走一半弯路。工具是死的,排查思路是活的。多练几次,你也能做到看一眼日志就知道问题在哪。