it编程 > 数据库 > Mysql

MySQL慢查询优化从30秒到300毫秒的完整过程

5人参与 2025-12-09 Mysql

最近接手一个老项目,某个列表接口响应时间30秒,用户疯狂投诉。

排查下来是sql问题,优化后降到300毫秒。记录一下完整过程。

一、发现问题

1.1 开启慢查询日志

-- 查看是否开启
show variables like 'slow_query%';
show variables like 'long_query_time';

-- 开启慢查询日志
set global slow_query_log = 'on';
set global long_query_time = 1;  -- 超过1秒记录
set global slow_query_log_file = '/var/log/mysql/slow.log';

1.2 分析慢查询日志

# 用mysqldumpslow分析
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# 输出
count: 1532  time=28.35s (43424s)  lock=0.00s (0s)  rows=100.0 (153200)
  select * from orders where user_id = n and status = n order by create_time desc limit n, n

找到了!这条sql执行了1532次,平均28秒。

二、分析sql

2.1 问题sql

select * 
from orders 
where user_id = 12345 
  and status = 1 
order by create_time desc 
limit 0, 20;

看起来很简单,为什么慢?

2.2 explain分析

explain select * from orders where user_id = 12345 and status = 1 order by create_time desc limit 0, 20;
+----+-------------+--------+------+---------------+------+---------+------+----------+-----------------------------+
| id | select_type | table  | type | possible_keys | key  | key_len | rows | filtered | extra                       |
+----+-------------+--------+------+---------------+------+---------+------+----------+-----------------------------+
|  1 | simple      | orders | all  | null          | null | null    | 5000000 | 0.10  | using where; using filesort |
+----+-------------+--------+------+---------------+------+---------+------+----------+-----------------------------+

问题暴露了:

2.3 查看表结构

show create table orders;
create table `orders` (
  `id` bigint not null auto_increment,
  `user_id` bigint not null,
  `status` tinyint not null default '0',
  `amount` decimal(10,2) not null,
  `create_time` datetime not null,
  `update_time` datetime not null,
  primary key (`id`)
) engine=innodb;

果然,只有主键索引,没有业务索引

三、优化方案

3.1 添加联合索引

-- 创建联合索引
alter table orders add index idx_user_status_time (user_id, status, create_time);

再次explain:

+----+-------------+--------+------+---------------------+---------------------+---------+-------------+------+-------+
| id | select_type | table  | type | possible_keys       | key                 | key_len | ref         | rows | extra |
+----+-------------+--------+------+---------------------+---------------------+---------+-------------+------+-------+
|  1 | simple      | orders | ref  | idx_user_status_time| idx_user_status_time| 9       | const,const | 156  | null  |
+----+-------------+--------+------+---------------------+---------------------+---------+-------------+------+-------+

完美:

3.2 为什么这样设计索引?

联合索引顺序:(user_id, status, create_time)

查询条件:where user_id = ? and status = ?
排序条件:order by create_time

索引匹配过程:
1. user_id = 12345  → 定位到用户的订单
2. status = 1       → 进一步过滤状态
3. create_time      → 索引本身有序,无需filesort

联合索引设计原则:

  1. 等值查询的列放前面
  2. 排序的列放最后
  3. 遵循最左前缀原则

3.3 优化效果

优化前:28.35秒,扫描500万行
优化后:0.003秒,扫描156行

提升:9000倍+

四、更多优化技巧

4.1 避免select *

-- 差:查询所有字段
select * from orders where ...

-- 好:只查需要的字段
select id, user_id, amount, create_time from orders where ...

好处:

4.2 覆盖索引

如果查询的字段都在索引里,不需要回表:

-- 索引:idx_user_status_time (user_id, status, create_time)

-- 这个查询可以用覆盖索引
select user_id, status, create_time from orders where user_id = 12345;

-- explain显示 using index

4.3 避免索引失效

-- ❌ 对索引列使用函数
select * from orders where date(create_time) = '2024-01-01';

-- ✅ 改写
select * from orders where create_time >= '2024-01-01' and create_time < '2024-01-02';

-- ❌ 隐式类型转换
select * from orders where user_id = '12345';  -- user_id是bigint

-- ✅ 类型一致
select * from orders where user_id = 12345;

-- ❌ like前置通配符
select * from orders where order_no like '%abc';

-- ✅ like后置通配符(可以用索引)
select * from orders where order_no like 'abc%';

4.4 分页优化

-- ❌ 深分页很慢
select * from orders order by id limit 1000000, 20;
-- 需要扫描100万行

-- ✅ 用游标分页
select * from orders where id > 1000000 order by id limit 20;
-- 直接定位,很快

五、索引设计原则

5.1 什么时候建索引?

场景是否建索引
where条件频繁查询的列✅ 是
order by排序的列✅ 是
join关联的列✅ 是
区分度低的列(如性别)❌ 否
频繁更新的列⚠️ 权衡

5.2 联合索引顺序

原则:
1. 区分度高的列放前面
2. 等值查询的列放前面
3. 排序列放最后

六、实用工具

6.1 慢查询分析

# mysqldumpslow
mysqldumpslow -s t -t 10 slow.log  # 按时间排序,前10条

# pt-query-digest(推荐)
pt-query-digest slow.log > report.txt

6.2 explain详解

type(重要,从好到差):
- system/const:常量查询
- eq_ref:主键/唯一索引
- ref:普通索引
- range:范围扫描
- index:索引全扫描
- all:全表扫描 ❌

extra(重要):
- using index:覆盖索引 ✅
- using where:需要回表过滤
- using filesort:额外排序 ⚠️
- using temporary:临时表 ⚠️

七、远程数据库排查技巧

有时候问题数据库在测试环境,本地连不上怎么办?

我的做法是用组网工具把本地和测试服务器连起来。之前用vpn,经常断还慢。现在用星空组网,本地直接连测试环境的mysql:

# 组网后直接用虚拟ip连接
mysql -h 192.168.188.10 -u root -p

explain、慢查询分析都能直接在本地跑,比登服务器方便多了。

总结

sql优化核心步骤:

1. 开启慢查询日志 → 发现问题sql
2. explain分析 → 定位问题原因
3. 添加/优化索引 → 解决问题
4. 再次explain → 验证效果
5. 线上执行 → 监控观察

记住几个原则:

以上就是mysql慢查询优化从30秒到300毫秒的完整过程的详细内容,更多关于mysql慢查询优化30秒到300毫秒的资料请关注代码网其它相关文章!

(0)

您想发表意见!!点此发布评论

推荐阅读

MySQL错误1005(errno: 150)的原因分析与解决方案

12-09

解决MySQL安装第四步报错问题(initializing database(may take a long time)

12-09

MySql 预处理(Preprocessor)的使用小结

12-09

MySQL下载时出现starting the server或initializing错误的原因分析及解决

12-09

mysql_mcp_server部署及应用实践案例

12-09

Mysql中RelayLog中继日志的使用

12-09

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论