SQL优化实战:从Explain执行计划到千万级数据查询调优全解析

慢查询拖垮整个系统?一个Explain就能定位90%的性能瓶颈。在实际开发中,我们每天都在和数据库打交道,但真正懂SQL调优的人却少之又少。很多人遇到查询慢的第一反应就是加索引,结果越加越慢,系统反而更卡。今天这篇文章,我会用真实案例带你走一遍从发现问题、分析问题到解决问题的完整流程,看完之后你会发现,SQL优化并没有想象中那么玄乎。

一、SQL优化为什么这么重要

在互联网公司里,数据库几乎是所有业务的核心引擎。订单系统、用户系统、日志系统,全部依赖数据库来读写数据。一旦某条SQL写得不好,轻则接口响应变慢,重则直接把数据库拖死,造成全线服务不可用。

我之前经历过一次线上事故:一条统计报表的SQL在凌晨跑批时把主库CPU干到了100%,导致订单创建接口全部超时,持续了将近20分钟。后来排查发现,就是一个简单的多表关联查询,少写了一个过滤条件,导致全表扫描了一张800万行的表。

所以说,SQL优化不是锦上添花的技能,而是每个后端开发必须掌握的基本功。

二、Explain:SQL优化的第一把钥匙

想要优化SQL,首先得学会"看病"。而MySQL提供的Explain命令,就是最好的诊断工具。很多人写了几年SQL,从来没用过Explain,这是非常可惜的。

我们先来看一个简单的案例。假设有一张用户订单表orders,结构如下:

字段名 类型 说明

id bigint 主键

user_id bigint 用户ID

status tinyint 订单状态

create_time datetime 创建时间

amount decimal 订单金额

现在有一条查询SQL:

sql

SELECT * FROM orders WHERE user_id = 1001 AND status = 1;

我们用Explain看一下它的执行计划:

+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+

| 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 5000 | Using where |

+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+

☆ 从这张结果中我们能读出几个关键信息:

1、type列显示为ALL,说明这条SQL进行了全表扫描,这是性能最差的情况。

2、possible_keys和key都为NULL,说明没有用到任何索引。

3、rows显示为5000,意味着MySQL需要逐行扫描5000条记录才能找到结果。

这个结果一目了然:这条SQL没有走索引,需要优化。

三、索引策略:不是建得越多越好

很多开发人员有一个误区,觉得索引建得越多查询就越快。其实恰恰相反,索引是一把双刃剑。

☆ 索引的优点很明显:

1、大幅提升查询速度,特别是范围查询和排序操作。

2、能够有效减少需要扫描的数据行数。

☆ 但索引也有代价:

1、每张表上的每个索引都会占用磁盘空间,索引越多占用越大。

2、写入操作(INSERT、UPDATE、DELETE)会变慢,因为每次写数据都要同步更新索引树。

3、过多的索引会让MySQL优化器在选择执行计划时产生困惑,反而可能选错索引。

回到刚才的例子,我们给user_id和status分别加上索引:

sql

ALTER TABLE orders ADD INDEX idx_user_id (user_id);

ALTER TABLE orders ADD INDEX idx_status (status);

再次执行Explain:

+----+-------------+--------+------+---------------------+-------------+---------+-------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+--------+------+---------------------+-------------+---------+-------+------+--------------------------+

| 1 | SIMPLE | orders | ref | idx_user_id,idx_status | idx_user_id | 8 | const | 120 | Using where; Using index |

+----+-------------+--------+------+---------------------+-------------+---------+-------+------+--------------------------+

☆ 这次的变化非常明显:

1、type从ALL变成了ref,说明走了非唯一索引扫描。

2、key列显示用到了idx_user_id这个索引。

3、rows从5000降到了120,扫描行数减少了97%以上。

不过这里还有一个问题:status这个字段的区分度很低,大部分订单的status都是1(已完成),所以单独建status索引效果并不好。更好的做法是建一个联合索引:

sql

ALTER TABLE orders DROP INDEX idx_user_id;

ALTER TABLE orders DROP INDEX idx_status;

ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

再看Explain结果:

+----+-------------+--------+------+---------------------+----------------+---------+-------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+--------+------+---------------------+----------------+---------+-------+------+--------------------------+

| 1 | SIMPLE | orders | ref | idx_user_status | idx_user_status | 16 | const | 45 | Using where; Using index |

+----+-------------+--------+------+---------------------+----------------+---------+-------+------+--------------------------+

☆ 联合索引的效果更好:

1、rows进一步降到了45行,说明MySQL精准定位到了需要的数据。

2、Extra中出现了Using index,说明这是一个覆盖索引,连回表操作都省了。

四、查询优化案例:一个订单统计SQL的调优之路

下面分享一个我在实际项目中遇到的真实案例。

业务需求是:查询最近30天内,每个用户的订单总金额,并且只统计金额大于100元的订单。

最初开发写的SQL是这样的:

sql

SELECT user_id, SUM(amount) as total_amount

FROM orders

WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)

AND amount > 100

GROUP BY user_id

ORDER BY total_amount DESC

LIMIT 10;

这条SQL在数据量只有几万行的时候跑得还行,但当订单表增长到500万行之后,查询时间直接飙到了8秒多。

💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。

你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!

希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!

感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。

博文入口:https://blog.csdn.net/Start_mswin 复制到【浏览器】打开即可,宝贝入口:https://pan.quark.cn/s/b42958e1c3c0 宝贝:https://pan.quark.cn/s/1eb92d021d17

作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~

📋 复制整篇文章

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐