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 |
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+

问题分析:

  1. type=ALL表示全表扫描
  2. Using filesort说明需要额外排序
  3. 现有索引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. 避坑指南:优化中的常见误区

  1. 过度索引:每个索引都会降低写操作速度,更新频繁的表要谨慎
  2. 盲目添加内存:当数据无法全部缓存在内存时,盲目增加内存收效甚微
  3. 过早优化:在系统早期阶段过度优化可能适得其反
  4. 忽略执行计划:没有结合EXPLAIN分析直接修改查询语句
  5. 冷热数据不分:频繁访问的历史数据未做归档处理

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光片诊断病情,但真正的治疗需要结合多种手段:

  1. 定期进行慢查询分析(建议每周至少一次)
  2. 建立性能基准和监控报警机制
  3. 重要变更前做性能压测
  4. 保持数据库统计信息的准确性
  5. 建立SQL审核流程,防范于未然

记住:数据库优化不是一劳永逸的工作,而是一个持续改进的过程。就像保持身体健康需要定期体检和科学锻炼,数据库性能也需要持续的监控和优化维护。