问题排查总论:流程、工具与最佳实践

索引审计日志的巡检,说白了就是从海量日志里捞出有问题的盲索引。我自己做了多年数据库审计,发现很多团队卡在这一步——不是查不出来,是查出来不会定位。嗯,这章我们就聊聊排查的整套打法。

排查前的准备工作

动手之前,先把环境摸清楚。我个人习惯先确认三个基本盘:

  • 日志源是否完整:审计日志有没有被截断?我曾经遇到过一次,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 参数后,脏页读一眼暴露。

最佳实践——我踩过的坑

避坑指南,都是真金白银换来的。我直接列几条最常见的,你对照看看有没有中招。

  1. 不要只看总命中率。有个索引命中率99.9%,但剩下0.1%是查全表的热点查询。我在项目中遇到过,那个索引其实是“伪优”,废了。
  2. 盲索引不一定是多余的。有时候索引本身没问题,是SQL写法导致无法使用——比如函数包裹索引列。这种情况别急着删索引,先改SQL。
  3. 警惕“一眼盲”陷阱。日志里出现大量全表扫描,不一定是索引问题,也可能是并发锁导致索引查询退化。我曾经被这个坑过,后来加了 pg_stat_activity 的实时快照才抓到元凶。
  4. 验证阶段分两步:先在预发布环境跑,再慢放生产流量。安全第一,别直接删生产索引。

重要提醒:在生产环境执行 DROP INDEX 前,务必确认该索引没有被任何外键或唯一约束引用。我有个同事曾经误删了唯一约束的底层索引,导致数据插入失败,回滚花了两小时。

如何快速定位“真凶”

排查到最后,你会发现大多数盲索引都逃不过三种模式。我总结成了下面的快速定位清单,你可以当成检查表用。

  • 情景A:索引未被使用 → 检查 seq_scan 是否 > idx_scan,可能索引坏了或统计信息过期。
  • 情景B:索引被使用但效率低 → 看 idx_tup_read / idx_tup_fetch 比例,如果很多读取但返回行少,说明索引选择性差。
  • 情景C:索引占用空间过大 → 对比 pg_relation_size 和实际数据量,可能包含大量冗余字段。

为什么要把这几种情景分开?因为治疗手段完全不同。情景A需要 ANALYZE 或重建索引,情景B可能需要改成覆盖索引,情景C则要删掉不必要的包含列。

嗯,说到这就差不多了。记住一个原则:没有银弹,但一套清晰的流程能让你少走一半弯路。工具是死的,排查思路是活的。多练几次,你也能做到看一眼日志就知道问题在哪。