1. 慢查询日志的"体检报告"功能
想象一下MySQL数据库就像一辆跑车,慢查询日志就是车上的行车记录仪。当我们的数据库突然变慢时,这个日志能帮我们精准定位到是哪个"零件"出了问题。通过分析超过指定时间阈值的SQL语句,我们可以像医生看体检报告一样找出系统的健康问题。
示例:启用慢查询日志(MySQL 5.7+)
-- 动态设置(重启失效)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2; -- 单位:秒
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';
-- 永久生效配置(my.cnf)
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1 -- 记录未使用索引的查询
这个配置相当于给数据库装上了"心电图监测仪",所有执行时间超过2秒的查询都会被记录。特别注意log_queries_not_using_indexes
这个参数,它像安检仪一样能发现那些偷懒不走索引的查询。
2. 日志分析的"破案"三板斧
2.1 原始日志直读法
直接查看原始日志文件就像翻阅案卷的原始笔录,虽然直观但效率较低。适合小规模快速排查。
典型日志片段:
# Time: 2023-08-20T09:10:21.123456Z
# User@Host: shop_user[shop_user] @ [192.168.1.100] Id: 12345
# Query_time: 5.123456 Lock_time: 0.001234 Rows_sent: 0 Rows_examined: 1000000
SET timestamp=1692520221;
UPDATE order_items SET status = 2 WHERE create_time < '2023-07-01';
这段日志告诉我们:某个更新语句扫描了100万行但只修改了0行,执行时间超过5秒。这就像发现仓库管理员为了找一件旧货翻遍了整个仓库。
2.2 统计分析利器:mysqldumpslow
MySQL自带的日志分析工具就像数据统计专员,能快速生成汇总报告。
使用示例:
# 查看执行时间最长的10个查询
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log
# 分析锁时间最长的查询
mysqldumpslow -s l -t 5 /var/lib/mysql/slow.log
# 统计出现次数最多的慢查询
mysqldumpslow -s c -t 20 /var/lib/mysql/slow.log
输出示例:
Count: 15 Time=3.24s (48s) Lock=0.00s (0s) Rows=100.0 (1500), shop_user[shop_user]@[192.168.1.100]
SELECT * FROM orders WHERE user_id=N AND status IN ('PENDING','PROCESSING')
这显示同一个查询模式执行了15次,总耗时48秒。相当于发现某个收银台总是排长队,需要重点处理。
2.3 进阶分析:Percona Toolkit
对于复杂场景,Percona的pt-query-digest就像专业的数据分析师,能提供深度分析报告。
分析示例:
pt-query-digest /var/lib/mysql/slow.log > slow_report.txt
报告中的关键指标:
Rank
:问题严重程度排名Response time
:累计响应时间占比Calls
:调用次数R/Call
:每次调用平均耗时Item
:查询特征(指纹)
3. 实战优化案例:电商订单查询优化
问题场景: 某电商平台在促销期间出现订单查询接口超时,慢查询日志发现如下典型问题:
SELECT * FROM orders
WHERE user_id = 12345
AND create_time BETWEEN '2023-08-01' AND '2023-08-20'
ORDER BY order_id DESC
LIMIT 10;
执行时间:2.8秒
诊断过程:
EXPLAIN SELECT * FROM orders
WHERE user_id = 12345
AND create_time BETWEEN '2023-08-01' AND '2023-08-20'
ORDER BY order_id DESC
LIMIT 10;
-- 输出结果:
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
| 1 | SIMPLE | orders | ALL | user_id | NULL | NULL | NULL | 987654 | Using where; Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
问题分析:
type=ALL
表示全表扫描Using filesort
说明需要额外排序- 现有索引
user_id
单列索引无法满足组合查询条件
优化方案:
-- 创建复合索引
ALTER TABLE orders ADD INDEX idx_user_create (user_id, create_time);
-- 优化后EXPLAIN结果:
+----+-------------+--------+-------+---------------+-----------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+-----------------+---------+------+------+-------------+
| 1 | SIMPLE | orders | range | idx_user_create | idx_user_create | 10 | NULL | 123 | Using where |
+----+-------------+--------+-------+---------------+-----------------+---------+------+------+-------------+
优化效果:
- 执行时间从2.8秒降至0.05秒
- 扫描行数从98万行降至123行
- 消除文件排序带来的性能损耗
4. 慢查询优化的"武功心法"
4.1 索引优化黄金法则
- 左前缀原则:复合索引(a,b,c)相当于同时创建了(a), (a,b), (a,b,c)三个索引
- 覆盖索引:SELECT的字段尽量包含在索引中
- 索引选择性:选择区分度高的列建立索引,如手机号比性别更适合建索引
反面案例:
-- 在500万用户表中查询性别为男的用户
SELECT * FROM users WHERE gender = 'M';
-- 即使有gender索引,由于需要回表查询,效率可能不如全表扫描
4.2 查询语句优化技巧
- **避免SELECT ***:只获取必要字段,减少数据传输量
- 分页优化:避免大偏移量分页
-- 低效写法
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;
-- 优化写法(基于游标)
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;
- 批量操作:用IN代替多个OR
-- 低效写法
SELECT * FROM products WHERE category_id = 1 OR category_id = 3 OR category_id = 5;
-- 优化写法
SELECT * FROM products WHERE category_id IN (1,3,5);
4.3 参数调优要点
# my.cnf 关键参数
query_cache_type = 0 # 在高并发写入场景建议关闭查询缓存
innodb_buffer_pool_size = 16G # 通常设置为物理内存的60-70%
max_connections = 500 # 根据实际需要设置
thread_cache_size = 32 # 减少线程创建开销
5. 避坑指南:优化中的常见误区
- 过度索引:每个索引都会降低写操作速度,更新频繁的表要谨慎
- 盲目添加内存:当数据无法全部缓存在内存时,盲目增加内存收效甚微
- 过早优化:在系统早期阶段过度优化可能适得其反
- 忽略执行计划:没有结合EXPLAIN分析直接修改查询语句
- 冷热数据不分:频繁访问的历史数据未做归档处理
6. 关联技术拓展:ORM框架的优化
以MyBatis为例,常见的性能陷阱:
<!-- 模糊查询的两种写法对比 -->
<!-- 低效写法(导致全表扫描) -->
<select id="searchProducts" parameterType="String" resultType="Product">
SELECT * FROM products WHERE name LIKE '%${keyword}%'
</select>
<!-- 优化写法(使用索引) -->
<select id="searchProducts" parameterType="String" resultType="Product">
SELECT * FROM products WHERE name LIKE CONCAT(#{keyword}, '%')
</select>
7. 总结:性能优化的系统工程
慢查询日志分析就像医生通过X光片诊断病情,但真正的治疗需要结合多种手段:
- 定期进行慢查询分析(建议每周至少一次)
- 建立性能基准和监控报警机制
- 重要变更前做性能压测
- 保持数据库统计信息的准确性
- 建立SQL审核流程,防范于未然
记住:数据库优化不是一劳永逸的工作,而是一个持续改进的过程。就像保持身体健康需要定期体检和科学锻炼,数据库性能也需要持续的监控和优化维护。