Skip to main content

如果一个表在刚创建时没有慢 SQL,后面突然出现了慢 SQL,怎么处理?

这是一个非常典型的数据库性能“突刺”问题。刚创建时快,说明表结构索引设计没问题;突然变慢,说明“环境变量”变了。

处理这类问题必须分两步走:先“救火”止损,后“查因”根治。以下是标准处理SOP(以MySQL为例,原理通用):

第一步:应急止血(先恢复业务)

如果慢SQL正在拖垮线上业务,不要犹豫,立即执行:

  1. 杀掉慢查询会话: 执行 SHOW PROCESSLIST; 找到处于 Sending dataCreating sort index 状态且耗时久的 Id,执行 KILL [Id];
  2. 紧急绑定执行计划(SQL Plan Baseline): 如果业务无法重启且SQL高频调用,立即使用 USE INDEXFORCE INDEX 强制走索引,或者通过 SQL Hint(如 Oracle 的 Outline / MySQL 8.0 的 Query Rewrite Plugins)把执行计划固定住。
  3. 临时扩容/限流:若CPU飙高,临时升级实例规格,或在网关层对该API进行限流,防止雪崩。

第二步:深度诊断(核心四板斧)

止血后,按以下顺序排查根本原因,90%的问题都集中在前三斧

1. 统计信息过期(最可能的第一嫌疑人)

表刚创建时数据量小,全表扫描和走索引代价差不多。随着数据增长,**优化器依赖的“统计信息”**若未及时更新,会误判表只有几百行,从而选择错误的索引(或不用索引)。

  • 验证:执行 EXPLAIN [慢SQL],观察 rows 估算值是否严重偏离实际行数。
  • 处理:执行 ANALYZE TABLE [表名]; 手动更新统计信息,让优化器重新计算索引基数(Cardinality)。这是最温和且见效最快的手段。

2. 索引失效(隐式类型转换或函数破坏)

SQL没变,但表字段变了,或者传入的参数变了。

  • 隐式转换WHERE phone = 13800138000,但 phone 字段是 varchar 类型,导致索引列被隐式转为数字,索引失效。
  • 函数操作WHERE DATE(create_time) = '2026-06-17',对索引列用了函数,导致无法走索引。
  • 处理:修正应用传参类型,或改为范围查询(create_time >= '...' and < '...')。

3. 数据倾斜(直方图缺失)

传入的参数值分布不均。例如 status 字段索引,大多数是 1(已完成),少量是 0(待处理)。以前查 0 很快,现在查 0 过滤出大量数据,优化器错误地选择了回表代价更大的索引。

  • 处理:若MySQL 8.0,可创建直方图(ANALYZE TABLE t UPDATE HISTOGRAM ON status);或者针对倾斜值单独改写SQL。

4. 锁等待与元数据锁(MDL)

SQL本身执行很快(0.01秒),但突然变慢(显示等待状态为 Waiting for table metadata lock)。

  • 场景:有人对大表执行了 ALTER TABLEOPTIMIZE TABLE 未完成,或者长事务未提交,阻塞了所有对该表的DML/查询。
  • 处理:查询 performance_schema.metadata_locks 找到阻塞源,KILL 掉持有锁的线程或DDL操作。

第三步:深度挖掘(执行计划对比)

务必对比“刚创建时”和“现在”的执行计划

  • 检查 Explain 中的 type 是否从 ref/range 退化为了 ALL(全表扫描)。
  • 检查 Extra 字段是否突然出现了 Using temporaryUsing filesort(说明排序内存不足或索引无法覆盖排序)。
  • 检查索引选择性:执行 SHOW INDEX FROM [表名];,查看 Cardinality(基数)。如果该值远小于表行数,说明该索引已失去区分度,应考虑新建联合索引或删除冗余干扰索引。

第四步:根治与防范(长效机制)

  • 归档历史数据:如果表数据量已突破亿级,B+树高度从3层变为4层,IO次数增加。需做冷热分离,将旧数据迁移至归档表或历史库。
  • 调整参数:增大 join_buffer_sizesort_buffer_size(如果排序慢);调整 innodb_buffer_pool_size 减少物理读。
  • 监控告警:开启慢查询日志(long_query_time=1),配置 pt-query-digest 定期分析;同时监控 rows_examined(扫描行数)的环比突增,一旦超过阈值自动告警。

总结一句话ANALYZE TABLE 碰碰运气(命中率40%),再查 EXPLAIN 看索引选择(命中率80%),最后查 PROCESSLIST 看MDL锁(命中率95%)。 如果以上都无效,极大概率是业务并发量突然暴增导致资源争用(CPU/IO饱和),此时只能扩容或读写分离。