- 工信部备案号 滇ICP备05000110号-1
- 滇公网安备53011102001527号
- 增值电信业务经营许可证 B1.B2-20181647、滇B1.B2-20190004
- 云南互联网协会理事单位
- 安全联盟认证网站身份V标记
- 域名注册服务机构许可:滇D3-20230001
- 代理域名注册服务机构:新网数码
- CN域名投诉举报处理平台:电话:010-58813000、邮箱:service@cnnic.cn
生产环境中,数据库响应变慢时,很多运维人员第一反应就是增加索引。但实际上,索引并非万能,只有准确分析 SQL 的执行过程,才能真正定位性能瓶颈。
下面以一个典型的订单表为例,介绍慢 SQL 的排查思路。
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
status TINYINT NOT NULL,
amount DECIMAL(10,2),
create_time DATETIME NOT NULL,
INDEX idx_user(user_id),
INDEX idx_create_time(create_time)
) ENGINE=InnoDB;
业务查询:
SELECT *
FROM orders
WHERE user_id = 10001
AND status = 1
ORDER BY create_time DESC
LIMIT 20;
随着数据增长到数千万行,该 SQL 响应时间超过 3 秒。
首先查看执行计划:
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 10001
AND status = 1
ORDER BY create_time DESC
LIMIT 20;
输出示例:
type: ref
key: idx_user
rows: 385214
Extra: Using where; Using filesort
重点关注几个字段:
type
访问方式
rows
预计扫描行数
key
实际使用索引
Extra
是否存在额外开销
这里最大的信号是:
Using filesort
说明排序无法利用索引,需要额外排序。
当前只有单列索引:
user_id
create_time
对于上述 SQL,更合适的是联合索引:
ALTER TABLE orders
ADD INDEX idx_user_status_time (
user_id,
status,
create_time
);
再次查看执行计划:
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 10001
AND status = 1
ORDER BY create_time DESC
LIMIT 20;
输出变为:
type: ref
key: idx_user_status_time
rows: 20
Extra: Using index
扫描行数从几十万降到几十行,排序也由索引完成。
MySQL 8.0 提供了更直观的执行分析。
EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE user_id = 10001
AND status = 1
ORDER BY create_time DESC
LIMIT 20;
示例输出:
-> Limit: 20 row(s)
(actual time=0.842..0.857 rows=20 loops=1)
-> Index lookup
(actual time=0.824..0.841 rows=20)
相比普通 EXPLAIN,EXPLAIN ANALYZE 会显示:
真实执行时间
实际扫描行数
每一步耗时
循环次数
能够快速判断预估值与真实执行情况是否一致。
如果查询:
SELECT *
即使使用索引,也可能发生回表。
例如:
SELECT id,user_id,status,create_time
FROM orders
WHERE user_id=10001
ORDER BY create_time DESC;
如果查询字段全部包含在联合索引中,就有机会使用覆盖索引。
执行计划:
Extra: Using index
意味着无需回表读取数据页,性能通常更高。
下面的 SQL 很容易导致索引无法使用:
SELECT *
FROM orders
WHERE DATE(create_time)='2026-07-01';
由于对索引列进行了函数计算,MySQL 无法利用索引。
推荐改写:
SELECT *
FROM orders
WHERE create_time >= '2026-07-01 00:00:00'
AND create_time < '2026-07-02 00:00:00';
执行计划通常会恢复为索引范围扫描。
开启慢查询日志:
slow_query_log = ON
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = ON
查看最耗时 SQL:
mysqldumpslow -s t -t 20 /var/lib/mysql/slow.log
按平均耗时排序:
mysqldumpslow -s at -t 20 /var/lib/mysql/slow.log
如果需要更详细的分析,可使用:
pt-query-digest /var/lib/mysql/slow.log
它会统计:
执行次数
平均耗时
95%响应时间
扫描行数
锁等待时间
非常适合持续优化数据库性能。
售前咨询
售后咨询
备案咨询
二维码

TOP