数据库性能暴降这事儿,我碰见过太多次了。前阵子有个朋友半夜打电话过来,说他们公司的电商系统突然卡得像幻灯片,用户疯狂投诉,老板在群里骂人。他查了半天监控,CPU、内存、磁盘都看着正常,就是数据库响应时间从 20 毫秒飙到了 5 秒钟。这种场景我太熟悉了——问题往往不在表面,而在那些容易被忽略的角落里。今天我就把这几年积累的 5 个排查技巧掰开揉碎讲给你听,全是实战中验证过的东西。

第一个技巧,先别急着看慢查询日志,而是去抓当前正在运行的 SQL。很多人遇到性能问题,第一反应就是翻慢查询日志,但慢查询记录的是过去的问题,你看到的时候,罪魁祸首可能已经跑完了。正确做法是直接连上数据库,用 (MySQL)或 (PostgreSQL)这类命令,看看当前哪些查询在占用资源。我见过最典型的一次,有个凌晨的定时任务跑了全表扫描,把整个数据库锁住了。慢查询日志根本看不到这个任务,因为它只记录超过阈值的查询,而那个全表扫描每秒只返回几十行数据,根本没超阈值。抓当前 SQL 时,重点关注 字段显示 “Sending data” 或 “Locked” 的线程,尤其是运行时间超过 10 秒的。把这些线程的 ID 记下来,再用 分析执行计划,基本就能锁定问题。
第二个技巧,别光盯着数据库本身,要检查应用层的连接池配置。有一次我帮一个创业团队排查问题,数据库 CPU 占用率一直很高,但查询量并不大。我一看应用配置,连接池最大连接数设成 200,而数据库的最大连接数才 300。这本身没问题,问题在于连接池的 参数设成了 30 秒,意味着应用在等待空闲连接时会阻塞 30 秒才报错。结果前端请求堆积,数据库连接被占满,新请求全部排队。更坑的是,连接池的超时时间和数据库的 不匹配,数据库这边把空闲连接回收了,应用那边还以为连接还能用,发请求时直接报错。解决办法是:连接池最大连接数设为数据库最大连接数的 80% 左右, 控制在 100 毫秒以内,同时定期检测空闲连接的有效性。很多性能问题其实不是数据库扛不住,而是应用层的连接池管理不当。
第三个技巧,检查索引使用情况,但别只看有没有索引,要看索引是否真的被用上了。我见过太多人给表加了一堆索引,以为万事大吉,结果一查,很多查询根本没用索引,直接走全表扫描。怎么验证?用 分析 SQL,看看 (或 )字段。如果是 ,说明全表扫描; 表示只扫描索引树,效率可能仍不如走索引;、、 才是理想状态。更隐蔽的问题是索引失效,例如在 条件里对索引列做函数操作, 会让索引失效,应该写成 。还有联合索引的“最左前缀”原则,查询条件若没有使用索引的第一个字段,索引也白搭。建议定期用 之类的工具扫描慢查询日志,找出哪些索引是摆设,哪些查询缺少索引。
第四个技巧,关注数据库的锁等待和死锁情况。很多性能问题不是查询本身慢,而是查询在等锁。MySQL 的 InnoDB 引擎默认行级锁,但如果在 或 时没有走索引,行锁会升级为表锁。想象一下:一个未走索引的 把整张表锁住,其他所有对该表的操作都只能排队。排查锁问题时,可以在 MySQL 里用 查看 部分,或开启 参数记录所有死锁。更主动的做法是把 设置为合理的值,例如 5 秒,避免线程无限期等待。一次我处理订单系统时,发现高峰期大量请求卡在同一个商品库存的 上,原因是并发更新同一行记录导致行锁竞争激烈。解决方案是把库存字段拆成多个分片,每个分片独立更新,最后再汇总,虽然逻辑稍复杂,但并发能力提升了约 10 倍。
第五个技巧,检查磁盘 I/O 和内存命中率。很多人觉得数据库慢就是查询慢,实际上磁盘读写瓶颈才是隐形杀手。用 或 看磁盘的 和 指标。如果 大于 10 毫秒,说明磁盘响应时间有问题;如果 与 差距很大,说明 I/O 请求在排队。内存方面,关注缓冲池的命中率。InnoDB 缓冲池命中率应保持在 99% 以上,低于这个值说明内存不足,数据库频繁从磁盘读取数据。可以通过 和 两个值计算命中率。命中率低时,要么加内存,要么优化查询减少数据读取量。还有一种常见情况:使用机械硬盘时,数据库频繁写 redo log 导致磁盘 I/O 打满。这时可以把 设为 2(每秒写一次日志),可以大幅降低写磁盘频率,但会有最多 1 秒的数据丢失风险。生产环境建议仍保持默认的 1,并使用 SSD 以获得更好的性能。
这 5 个技巧,每个都对应一个常见的性能坑。我的经验是,遇到数据库性能问题,别急着调参数或加硬件,先按这个顺序排查:当前 SQL、连接池、索引、锁、磁盘和内存。很多时候,问题就出在这些地方。而且你会发现,90% 的性能问题其实不是数据库本身不行,而是使用姿势不对——比如索引没用对、连接池配置不合理、查询写成全表扫描。这些坑只要稍微注意,就能避免。说句实在话,数据库运维这东西,经验比理论重要得多。踩过的坑越多,下次遇到问题就越淡定。


