1. MySQL慢查询日志:数据库性能优化的第一道防线
每次接手性能堪忧的MySQL数据库时,我的第一个动作永远是检查慢查询日志。这个看似简单的功能,曾帮我定位过无数隐藏的性能杀手——从缺失的索引到失控的全表扫描,从低效的联接到臃肿的存储过程。作为DBA和开发者的必备工具,慢查询日志就像数据库的"黑匣子",记录着所有执行时间超过阈值的SQL语句。
在电商大促前的一次压测中,我们通过慢查询日志发现某个商品列表接口的SQL竟然需要8秒执行,排查后发现是缺失了组合索引。加上索引后响应时间直接降到200毫秒以内。这就是为什么我总说:不懂慢查询日志的优化,就像蒙着眼睛调优。
2. 慢查询日志核心机制解析
2.1 工作原理与记录内容
MySQL慢查询日志的实现远比表面看起来的精妙。当long_query_time阈值设为2秒时,任何执行时间超过2秒的SQL都会被记录——但这里有个关键细节:计时起点是语句实际执行开始时,而非收到请求的时间。这意味着排队等待锁的时间不会被计入。
日志中每条记录包含15个关键字段:
# Time: 2023-07-20T15:23:45.123456Z # User@Host: app_user[app_user] @ [192.168.1.100] # Query_time: 12.345678 Lock_time: 0.001234 Rows_sent: 1 Rows_examined: 1000000 SET timestamp=1689866625; SELECT * FROM orders WHERE status='pending' AND created_at < '2023-01-01';特别要注意Rows_examined与Rows_sent的比值。上例中扫描100万行仅返回1条数据,典型索引缺失案例。
2.2 参数配置黄金法则
我的生产环境标准配置模板:
slow_query_log = ON slow_query_log_file = /var/lib/mysql/mysql-slow.log long_query_time = 1 # 电商类建议0.5-1秒,ERP类可放宽到2秒 log_queries_not_using_indexes = ON # 捕获全表扫描 log_throttle_queries_not_using_indexes = 10 # 避免日志爆炸 log_slow_admin_statements = ON # 记录慢的ALTER等管理语句 log_slow_slave_statements = ON # 主从环境下启用警告:在QPS过万的系统上开启
log_queries_not_using_indexes可能导致I/O瓶颈,建议先在测试环境评估影响。
3. 慢查询分析实战技巧
3.1 日志分析三板斧
3.1.1 mysqldumpslow工具
# 按查询时间排序 mysqldumpslow -s t /var/lib/mysql/mysql-slow.log # 统计出现次数TOP10的慢查询 mysqldumpslow -s c -t 10 /var/lib/mysql/mysql-slow.log # 解析特定类型的查询 mysqldumpslow -g "ORDER BY" /var/lib/mysql/mysql-slow.log3.1.2 pt-query-digest进阶分析
Percona Toolkit中的这个工具能生成HTML报告:
pt-query-digest \ --filter '$event->{fingerprint} =~ m/^SELECT/' \ --limit=10 \ /var/lib/mysql/mysql-slow.log > slow_report.html报告会标注哪些查询存在Filesort或Temporary table,这些都是优化重点。
3.2 经典优化案例库
案例1:分页查询优化
原始慢查询:
SELECT * FROM user_activities ORDER BY created_at DESC LIMIT 100000, 20; -- 扫描100020行优化方案:
SELECT * FROM user_activities WHERE id > (SELECT id FROM user_activities ORDER BY created_at DESC LIMIT 100000, 1) ORDER BY created_at DESC LIMIT 20; -- 通过子查询先定位ID案例2:JSON字段查询
未优化的JSON操作:
SELECT * FROM products WHERE JSON_EXTRACT(specs, '$.weight') > 10; -- 全表扫描优化方案:
ALTER TABLE products ADD COLUMN weight DECIMAL(10,2) GENERATED ALWAYS AS (JSON_EXTRACT(specs, '$.weight')) STORED; CREATE INDEX idx_weight ON products(weight);4. 生产环境运维要点
4.1 日志轮转策略
通过logrotate实现每日切割并压缩历史日志:
/var/lib/mysql/mysql-slow.log { daily rotate 30 compress delaycompress missingok notifempty create 660 mysql mysql postrotate mysqladmin flush-logs endscript }4.2 监控告警方案
使用Prometheus+Alertmanager监控慢查询增长趋势:
# prometheus.yml 配置示例 scrape_configs: - job_name: 'mysql_slow_queries' metrics_path: /probe params: module: [mysql_slow_log] static_configs: - targets: ['192.168.1.100:9104']对应的告警规则:
groups: - name: mysql-slow-alert rules: - alert: HighSlowQueries expr: increase(mysql_slow_queries_total[1h]) > 50 for: 10m labels: severity: warning annotations: summary: "突增慢查询检测 (instance {{ $labels.instance }})"5. 性能优化深度策略
5.1 执行计划分析技巧
对慢查询使用EXPLAIN FORMAT=JSON获取详细信息:
EXPLAIN FORMAT=JSON SELECT o.* FROM orders o JOIN users u ON o.user_id = u.id WHERE u.vip_level > 3 AND o.status = 'shipped'\G重点关注:
key_len:使用的索引长度ref:列比较值Extra:Using filesort/temporary等警告
5.2 索引优化矩阵
根据查询模式设计索引:
| 查询条件 | 排序字段 | 推荐索引 |
|---|---|---|
| WHERE a=? AND b>? | ORDER BY c | INDEX(a,b,c) |
| WHERE a IN (?) | ORDER BY b | INDEX(a,b) |
| WHERE a=? AND b=? | - | INDEX(a,b)或INDEX(b,a) |
对于JSON字段的索引,MySQL 8.0+支持函数索引:
CREATE INDEX idx_name ON users((CAST(data->>'$.name' AS CHAR(30))));6. 云数据库特殊考量
AWS RDS/Aliyun RDS等托管服务需注意:
- 参数组修改需要重启生效
- 日志文件位置可能不同(如RDS的
/rdsdbdata/log/slow/mysql-slow.log) - 可能需要通过控制台开启日志导出
- 性能洞察(Performance Insights)可能替代部分慢查询功能
Azure Database for MySQL的慢查询日志配置示例:
-- 通过SQL命令启用(需要超级权限) CALL mysql.az_set_configuration('slow_query_log', 'ON'); CALL mysql.az_set_configuration('long_query_time', '1');7. 慢查询日志的局限性
虽然强大,但慢查询日志也有其边界:
- 不记录被终止的查询(如超过
max_execution_time) - 二进制日志格式下不记录参数化查询的具体值
- 高频短查询可能不会触发记录但整体影响大
- 不显示执行计划变更历史
这时需要结合Performance Schema和sys schema:
-- 查看哪些全表扫描没被慢查询捕获 SELECT * FROM sys.statements_with_full_table_scans;8. 前沿实践:持续分析流水线
我在团队中建立的自动化分析流程:
- Filebeat收集慢查询日志到Kafka
- Flink实时解析SQL指纹(fingerprint)
- 写入Elasticsearch生成动态仪表盘
- 每日TOP10慢查询自动生成优化工单
关键实现代码片段:
# SQL指纹生成算法 def sql_fingerprint(sql): sql = re.sub(r'([\'"])(?:(?!\1).)*?\1', '?', sql) # 移除字符串 sql = re.sub(r'\b\d+\b', '?', sql) # 替换数字 sql = re.sub(r'\b0x[0-9a-f]+\b', '?', sql) # 替换十六进制 return sql.strip().lower()这种方案比传统工具快10倍以上,特别适合大型电商平台。