文章目录
-
- 一、问题背景:生产环境慢查询故障场景
- 二、慢查询调优核心流程与根因对比
-
- [2.1 纵向核心调优流程](#2.1 纵向核心调优流程)
- [2.2 横向慢查询根因对比](#2.2 横向慢查询根因对比)
- 三、分步实操落地与可运行代码
-
- [3.1 第一步:开启慢查询日志(MySQL原生操作)](#3.1 第一步:开启慢查询日志(MySQL原生操作))
- [3.2 第二步:自动分析慢查询日志(Python可运行代码)](#3.2 第二步:自动分析慢查询日志(Python可运行代码))
- [3.3 第三步:生产监控配置(YAML)](#3.3 第三步:生产监控配置(YAML))
- [3.4 第四步:业务层慢SQL埋点(TypeScript)](#3.4 第四步:业务层慢SQL埋点(TypeScript))
- [3.5 第五步:索引调优与量化性能对比](#3.5 第五步:索引调优与量化性能对比)
- 四、生产级部署方案与安全审计
-
- [4.1 慢查询日志安全配置](#4.1 慢查询日志安全配置)
- [4.2 索引上线安全流程](#4.2 索引上线安全流程)
- [4.3 定期安全审计](#4.3 定期安全审计)
- 五、技术前瞻性分析
- 六、附录:完整技术图谱
一、问题背景:生产环境慢查询故障场景
我在今年618大促前的电商平台压测中,遇到了典型的慢查询故障:用户中心「我的订单」接口,压测到QPS 200的时候,接口TP99直接冲到了3.2s,多次出现超时告警。排查后发现核心问题是订单表120万数据,查询语句走全表扫描,慢查询日志1小时内产生了上千条慢SQL。
经过完整的诊断和索引调优后,接口TP99降到了28ms,单实例支撑QPS提升到1200+,性能提升超过6倍,顺利扛住了大促流量。本文整理了从故障诊断到落地调优的完整可落地流程,新人也可以跟着步骤完成生产级调优。
二、慢查询调优核心流程与根因对比
本文原创双流程图,分别为纵向核心调优流程、横向慢查询根因对比:
2.1 纵向核心调优流程
是
否
是
否
生产环境性能告警
开启慢查询日志采集
提取TOP N慢SQL按总耗时排序
Explain解析执行计划
是否定位根因
制定索引优化方案
开启performance_schema分析锁/IO
灰度上线验证性能指标
指标符合要求?
全量上线完成调优
2.2 横向慢查询根因对比
慢查询根因分类
全表扫描
索引失效
回表次数过多
索引区分度差
添加匹配查询条件的索引
修复隐式转换/避免函数操作索引
调整联合索引顺序/使用覆盖索引
删除冗余索引/前缀索引优化
性能提升: 10-100倍
性能提升: 5-50倍
性能提升: 2-10倍
性能提升: 1-5倍
三、分步实操落地与可运行代码
3.1 第一步:开启慢查询日志(MySQL原生操作)
临时开启调试,重启失效:
sql
-- 开启慢查询日志
set global slow_query_log = 'ON';
-- 记录耗时超过1秒的查询
set global long_query_time = 1;
-- 只记录扫描超过1000行的查询,过滤无效日志
set global min_examined_row_limit = 1000;
-- 记录所有未使用索引的查询
set global log_queries_not_using_indexes = 'ON';
-- 查看慢查询日志路径
show variables like '%slow_query_log_file%';
持久化开启需要修改my.cnf/my.ini配置文件,添加以下配置后重启MySQL:
ini
slow_query_log = ON
long_query_time = 1
slow_query_log_file = /var/lib/mysql/slow.log
log_queries_not_using_indexes = ON
3.2 第二步:自动分析慢查询日志(Python可运行代码)
本文提供企业级自动分析脚本,自动聚合同类SQL,输出TOP 10慢SQL:
python
# 企业级慢查询日志自动分析脚本 Python3 可直接运行
import re
from collections import defaultdict
def analyze_slow_query_log(log_path: str, top_n: int = 10):
# 匹配通用格式的慢查询日志
pattern = re.compile(r'# Query_time: (\d+\.\d+)\s+Lock_time: (\d+\.\d+)\s+Rows_sent: (\d+)\s+Rows_examined: (\d+).*?\n(.*?);', re.DOTALL)
sql_stats = defaultdict(lambda: {"count": 0, "total_time": 0, "total_examined": 0})
with open(log_path, 'r', encoding='utf-8') as f:
content = f.read()
matches = pattern.findall(content)
for match in matches:
query_time = float(match[0])
rows_examined = int(match[3])
sql = match[4].strip().lower()
# 标准化SQL模板,聚合同结构不同参数的SQL
sql = re.sub(r'\?|\d+|\'.*?\'', '?', sql)
sql_stats[sql]["count"] += 1
sql_stats[sql]["total_time"] += query_time
sql_stats[sql]["total_examined"] += rows_examined
# 按总耗时排序输出TOP N
sorted_sql = sorted(sql_stats.items(), key=lambda x: x[1]["total_time"], reverse=True)[:top_n]
print(f"TOP {top_n} 慢SQL统计:")
for idx, (sql, stat) in enumerate(sorted_sql, 1):
avg_time = stat["total_time"] / stat["count"]
print(f"{idx}. 总耗时: {stat['total_time']:.2f}s 调用次数: {stat['count']} 平均耗时: {avg_time:.2f}s 总扫描行数: {stat['total_examined']}")
print(f"SQL模板: {sql[:200]}...\n")
if __name__ == "__main__":
# 修改为你的慢查询日志路径即可运行
analyze_slow_query_log("/var/lib/mysql/slow.log", top_n=10)
3.3 第三步:生产监控配置(YAML)
Prometheus + mysqld_exporter生产级慢查询监控告警配置:
yaml
# prometheus 慢查询监控告警配置 生产可直接使用
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'mysql-slow-monitor'
static_configs:
- targets: ['mysqld-exporter:9104']
metrics_path: '/metrics'
params:
collect[]:
- slowlog
- info_schema.processlist
- perf_schema.eventsstatements
# 告警规则
rule_files:
- "mysql_slow_rules.yml"
groups:
- name: mysql_slow_alert
rules:
- alert: MySQL慢查询数量过高
expr: increase(mysql_slow_queries[1m]) > 10
for: 5m
labels:
severity: critical
annotations:
summary: "实例 {{ $labels.instance }} 慢查询告警"
description: "过去5分钟慢查询总数量超过10次,请及时排查优化"
3.4 第四步:业务层慢SQL埋点(TypeScript)
Node.js业务层慢SQL埋点,用于APM上报:
typescript
// Node.js业务层慢SQL埋点 TS版本
import type { Connection } from 'mysql2/promise';
export async function queryWithSlowLog<T>(
sql: string,
connection: Connection,
threshold: number = 1000
): Promise<T> {
const startTime = Date.now();
try {
const [result] = await connection.query<T>(sql);
const cost = Date.now() - startTime;
if (cost > threshold) {
console.warn(`[Slow SQL Detected] 耗时: ${cost}ms SQL: ${sql.slice(0, 200)}`);
// 上报到内部APM平台
}
return result;
} catch (error) {
throw error;
}
}
3.5 第五步:索引调优与量化性能对比
我们以真实订单查询SQL为例,调优前SQL:
sql
SELECT order_id, total_amount, create_time
FROM orders
WHERE user_id = 12345 AND create_time BETWEEN '2024-01-01' AND '2024-06-01';
调优前只有主键索引,Explain结果显示type: ALL,全表扫描,调优后添加覆盖联合索引:
sql
-- 覆盖索引:把查询需要的字段都放到索引中,避免回表
CREATE INDEX idx_user_create_covering ON orders(user_id, create_time, order_id, total_amount);
调优前后性能量化对比如下:
| 指标项 | 调优前 | 调优后 | 提升倍数 |
|---|---|---|---|
| 单次查询平均耗时 | 1782ms | 11ms | 162倍 |
| 扫描行数 | 1268000 | 1240 | 1022倍 |
| 接口TP99延迟 | 3240ms | 28ms | 115倍 |
| 单实例支撑QPS | 187 | 1216 | 6.5倍 |
| 压测CPU使用率 | 78% | 12% | 6.5倍 |
四、生产级部署方案与安全审计
4.1 慢查询日志安全配置
-
权限控制 :慢查询日志可能包含用户敏感数据,设置权限
chmod 600 /var/lib/mysql/slow.log,仅mysql用户和DBA组可读。 -
自动轮转:配置logrotate自动轮转清理日志,避免撑爆磁盘,配置如下:
/var/lib/mysql/slow.log {
daily
rotate 7
missingok
compress
notifempty
create 600 mysql mysql
postrotate
systemctl reload mysql > /dev/null 2>&1 || true
endscript
} -
脱敏处理:慢查询日志带出生产环境前,必须脱敏手机号、身份证等敏感数据。
4.2 索引上线安全流程
- 大表加索引使用
pt-online-schema-change或gh-ost工具,避免直接ALTER TABLE锁表导致业务崩溃。 - MySQL 8.0+使用不可见索引灰度测试:
sql
-- 先创建不可见索引,不影响现有查询,测试性能
CREATE INDEX idx_user_create_covering ON orders(user_id, create_time) INVISIBLE;
-- 测试验证没问题后改为可见
ALTER TABLE orders ALTER INDEX idx_user_create_covering VISIBLE;
-- 有问题直接删除,不需要回滚大表操作
DROP INDEX idx_user_create_covering ON orders;
- 灰度流程:先在从库添加索引,切流量验证没问题后,再在主库添加。
4.3 定期安全审计
每月执行一次索引健康审计,清理冗余索引,减少写入开销,查询冗余索引的SQL:
sql
SELECT
CONCAT(s.TABLE_SCHEMA, '.', s.TABLE_NAME) AS table_name,
s.INDEX_NAME AS redundant_index
FROM information_schema.STATISTICS s
JOIN information_schema.STATISTICS s2
ON s.TABLE_SCHEMA = s2.TABLE_SCHEMA
AND s.TABLE_NAME = s2.TABLE_NAME
AND s.SEQ_IN_INDEX = s2.SEQ_IN_INDEX
AND s2.COLUMN_NAME = s.COLUMN_NAME
AND s.INDEX_NAME <> s2.INDEX_NAME
WHERE s.SEQ_IN_INDEX = 1
GROUP BY table_name, s.INDEX_NAME
HAVING COUNT(*) > 1;
五、技术前瞻性分析
- MySQL原生能力演进:MySQL 8.0已经支持直方图统计信息、降序索引、不可见索引,未来会逐步完善自动索引推荐能力,减少人工判断的误差。
- 云原生自动闭环调优:阿里云PolarDB、腾讯云TDSQL等云原生数据库已经推出自动索引优化功能,基于历史慢查询自动完成索引推荐、创建、淘汰,实现全流程闭环调优,大大降低DBA的工作量。
- 大模型驱动的智能调优:目前业内已经有不少团队把大模型接入数据库调优,输入慢查询和表结构就能自动生成优化方案,未来会实现从告警到调优上线全流程自动化,DBA仅需要做最终审核即可。
- 软硬件协同优化:随着NVMe SSD、持久化内存的普及,索引调优会从单纯的结构优化转向软硬件协同,进一步降低慢查询的延迟。
六、附录:完整技术图谱
MySQL慢查询调优技术图谱
故障诊断层
慢查询日志开启配置
TOP慢SQL聚合提取
Explain执行计划分析
Performance_schema性能分析
执行计划核心指标解读
根因分析层
无索引全表扫描
索引失效场景
隐式类型转换
函数操作索引列
左模糊查询
回表次数过多
冗余重复索引
索引基数区分度差
优化实施层
覆盖索引优化回表
联合索引顺序调整
前缀索引优化大字段
冗余索引清理
分库分表优化数据量
生产落地层
在线DDL工具使用
不可见索引灰度
日志安全轮转
敏感数据脱敏
慢查询监控告警
前沿技术层
自动索引推荐
大模型智能调优
云原生闭环调优
软硬件协同优化