1. 慢查询日志:数据库性能优化的第一道防线
作为一名常年与数据库打交道的开发者,我见过太多因为忽视慢查询而导致系统崩溃的案例。慢查询日志(Slow Query Log)就像是数据库系统的"黑匣子",它记录下所有执行时间超过预设阈值的SQL语句,为我们提供了优化数据库性能的第一手资料。想象一下,当你的应用突然变慢,用户开始抱怨,这时候慢查询日志就是你的"破案线索"。
MySQL的慢查询日志功能自5.1版本开始就作为核心功能存在,它能够精确记录执行时间超过long_query_time值的查询语句(包括查询时间、锁定时间、返回行数等关键信息)。不同于普通的错误日志或二进制日志,慢查询日志专门针对性能问题设计,是DBA和开发人员优化数据库性能的必备工具。
重要提示:在生产环境中开启慢查询日志会带来约5%-15%的性能开销,建议在问题排查期间开启,日常监控可选择抽样记录方式。
2. 慢查询日志的核心配置参数详解
2.1 基础配置参数
在my.cnf(Linux)或my.ini(Windows)配置文件中,以下参数控制着慢查询日志的行为:
[mysqld] slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 2 log_queries_not_using_indexes = 1 min_examined_row_limit = 100- slow_query_log:启用(1)或禁用(0)慢查询日志
- slow_query_log_file:日志文件路径(确保MySQL用户有写入权限)
- long_query_time:阈值(秒),执行时间超过此值的查询会被记录
- log_queries_not_using_indexes:记录未使用索引的查询(即使执行时间未超阈值)
- min_examined_row_limit:仅记录检查行数超过此值的查询
2.2 动态调整参数
在不重启MySQL服务的情况下,可以通过SET命令动态调整:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL log_queries_not_using_indexes = 'ON';实战经验:long_query_time的初始设置建议为2秒,优化过程中逐步下调至0.5秒。对于高并发OLTP系统,甚至可设为0.1秒。
2.3 日志轮换策略
慢查询日志会不断增长,需要定期清理或轮换:
# 日志轮换示例(配合Linux的logrotate) /var/log/mysql/mysql-slow.log { daily rotate 7 missingok compress delaycompress notifempty create 640 mysql mysql postrotate /usr/bin/mysqladmin flush-logs endscript }3. 慢查询日志分析实战
3.1 原始日志解读
一条典型的慢查询日志记录如下:
# Time: 2023-08-20T15:23:45.123456Z # User@Host: app_user[app_user] @ [192.168.1.100] # Query_time: 5.123456 Lock_time: 0.001234 Rows_sent: 1 Rows_examined: 1000000 SET timestamp=1692545025; SELECT * FROM orders WHERE customer_id = 100 AND status = 'pending' ORDER BY create_time DESC;各字段含义:
- Query_time:查询执行总时间
- Lock_time:等待表锁的时间
- Rows_sent:返回给客户端的行数
- Rows_examined:服务器层检查的行数
3.2 使用mysqldumpslow工具
MySQL自带的mysqldumpslow工具可以汇总慢查询日志:
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log常用参数:
- -s:排序方式(t按时间,c按次数,l按锁定时间)
- -t:显示前N条记录
- -g:使用正则过滤
输出示例:
Count: 5 Time=3.12s (15s) Lock=0.00s (0s) Rows=1.0 (5), app_user[app_user]@[192.168.1.100] SELECT * FROM orders WHERE customer_id=N AND status='pending' ORDER BY create_time DESC3.3 使用pt-query-digest进行高级分析
Percona Toolkit中的pt-query-digest提供更强大的分析功能:
pt-query-digest /var/log/mysql/mysql-slow.log输出包括:
- 查询响应时间占比
- 执行次数统计
- 单个查询的详细性能分析
- 建议的优化方向
4. 常见慢查询模式与优化策略
4.1 全表扫描问题
症状:
- Rows_examined远大于Rows_sent
- 日志中出现"Using filesort"或"Using temporary"
解决方案:
-- 添加复合索引 ALTER TABLE orders ADD INDEX idx_customer_status (customer_id, status); -- 优化后EXPLAIN验证 EXPLAIN SELECT * FROM orders WHERE customer_id = 100 AND status = 'pending';4.2 分页查询性能低下
典型慢查询:
SELECT * FROM large_table ORDER BY create_time LIMIT 1000000, 10;优化方案:
-- 方案1:使用覆盖索引 SELECT id FROM large_table ORDER BY create_time LIMIT 1000000, 10; SELECT * FROM large_table WHERE id IN (...); -- 方案2:记住上次查询位置 SELECT * FROM large_table WHERE create_time > '2023-08-01' ORDER BY create_time LIMIT 10;4.3 大表JOIN操作
问题查询:
SELECT * FROM users u JOIN orders o ON u.id = o.user_id WHERE u.register_time > '2023-01-01';优化步骤:
- 确保JOIN字段有索引
- 减少SELECT * 只查询必要字段
- 考虑分批次处理
ALTER TABLE orders ADD INDEX idx_user_id (user_id); SELECT u.name, o.order_id, o.amount FROM users u FORCE INDEX(PRIMARY) JOIN orders o FORCE INDEX(idx_user_id) ON u.id = o.user_id WHERE u.register_time > '2023-01-01';5. 生产环境慢查询监控体系
5.1 监控架构设计
+-------------------+ +----------------+ +-------------------+ | MySQL Slow Log | -> | Log Shipper | -> | Centralized | | (本地文件) | | (Filebeat/Fluentd)| | Storage (ES) | +-------------------+ +----------------+ +-------------------+ | +---------------+ | Visualization | | (Grafana/Kibana)| +---------------+5.2 基于Prometheus的监控方案
配置mysqld_exporter收集慢查询指标:
- name: mysql_slow_queries metrics_path: /metrics static_configs: - targets: ['localhost:9104'] params: slow_log: [ON]Grafana面板关键指标:
- 慢查询次数/分钟
- 平均慢查询时长
- 按模式分组的慢查询统计
- 索引命中率
5.3 慢查询告警规则
groups: - name: mysql-slow-alert rules: - alert: MySQLSlowQueryIncrease expr: rate(mysql_global_status_slow_queries[5m]) > 5 for: 10m labels: severity: warning annotations: summary: "MySQL慢查询激增 (instance {{ $labels.instance }})" description: "慢查询率当前为 {{ $value }} 次/分钟"6. 高级优化技巧与实战案例
6.1 查询重写技巧
案例1:IN子查询优化
-- 优化前 SELECT * FROM products WHERE category_id IN ( SELECT id FROM categories WHERE name LIKE '%电子%' ); -- 优化后 SELECT p.* FROM products p JOIN categories c ON p.category_id = c.id WHERE c.name LIKE '%电子%';案例2:OR条件优化
-- 优化前 SELECT * FROM users WHERE phone = '13800138000' OR email = 'test@example.com'; -- 优化后 SELECT * FROM users WHERE phone = '13800138000' UNION SELECT * FROM users WHERE email = 'test@example.com';6.2 索引优化策略
多列索引顺序原则:
- 等值查询字段优先
- 高区分度字段靠前
- 范围查询字段靠后
- 排序字段放在最后
示例:
-- 查询模式 SELECT * FROM orders WHERE shop_id = 100 AND status = 'completed' AND create_time BETWEEN '2023-01-01' AND '2023-08-20' ORDER BY amount DESC; -- 最优索引 ALTER TABLE orders ADD INDEX idx_shop_status_time_amount (shop_id, status, create_time, amount);6.3 临时表与内存优化
对于复杂查询,适当调整内存参数:
-- 增加排序缓冲区 SET SESSION sort_buffer_size = 4*1024*1024; -- 临时表大小 SET SESSION tmp_table_size = 64*1024*1024; SET SESSION max_heap_table_size = 64*1024*1024;7. 慢查询分析的常见误区
7.1 仅关注执行时间
不要只看Query_time,还需关注:
- Lock_time:表锁等待时间
- Rows_examined:检查的行数
- Rows_sent:实际返回的行数
- 是否使用临时表或文件排序
7.2 忽视事务的影响
一个在测试环境很快的查询,在生产环境可能变慢,因为:
- 长事务持有锁
- 事务隔离级别不同
- 并发连接数影响
7.3 过度依赖工具输出
自动化工具给出的建议并非总是最优:
- 索引建议可能忽略写操作成本
- 查询重写可能改变语义
- 需要结合业务场景判断
8. 从慢查询到性能优化的完整闭环
8.1 优化流程方法论
- 收集:持续记录慢查询日志
- 分析:识别高频/高消耗查询
- 诊断:EXPLAIN分析执行计划
- 优化:索引/查询重写/架构调整
- 验证:对比优化前后性能
- 监控:建立持续监控机制
8.2 性能基准测试
优化前后应进行基准测试:
sysbench oltp_read_write \ --db-driver=mysql \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=test \ --mysql-password=test \ --mysql-db=sbtest \ --tables=10 \ --table-size=100000 \ --time=300 \ --threads=32 \ --report-interval=10 \ run8.3 文档化与知识沉淀
建立团队内部的"慢查询知识库":
- 常见慢查询模式
- 已验证的优化方案
- 禁止使用的查询模式
- 索引使用规范
我在实际工作中发现,许多性能问题都是重复出现的。建立一个共享的优化案例库,可以显著提高团队处理慢查询的效率。比如我们团队维护的"十大禁忌查询"文档,新成员入职第一天就必须学习,这防止了很多潜在的性能问题。