Mysql 数据库使用入门系列:Mysql 查询优化,如何调整索引让查询更快(三)
- 作者: 刘杰
- 来源: 技术那些事
- 阅读:96
- 发布: 2025-07-26 11:31
- 最后更新: 2025-07-27 12:13
今天讲一下 Mysql 的查询优化相关的知识点,查询优化是 Mysql 使用进阶的必经之路。同样的表结构,索引设计需要完美配合业务查询,才能将业务的查询速度发挥到最优。
为什么要优化查询?要提升网站的响应速度,数据库查询无疑是关键一环。程序员都知道,一个 Web 页面上,可能涉及到多个查询动数据库中取数据,一般情况下,这些查询都是串行执行的,假设一个面涉及到查库 10 次,如果你的每条 sql 查询速度在 100 ms 以上,那不算传输和业务代码处理时间,你这个页面的最快加载完时间将在 100ms x 10 = 1s 以上。当然我这是举个例子,不可能每个sql 都这么慢,但是实际情况是每个页面也不仅仅10次查询,往往多到几十次。这就是需要优化数据查询的原因。
另外,Mysql 的查询优化,也是面试中的一个重点。如果你的简历中有 Mysql 数据库,那么面试官少不了会提问索引、查询优化相关的问题。如果这个公司的业务,偏向于数据处理方面,数据库的问题肯定是重中之重。甚至分库分表、分布式设计,或者根据一些业务场景,让你设计相关数据表的结构,这些问题肯定都少不了。
这篇文章,主要就是来讲解到底该如何优化 sql 查询,调整索引才能让查询更快。
Mysql 索引的适用场景
索引的核心作用是加速查询过滤(WHERE)、关联(JOIN)、排序(ORDER BY)、分组(GROUP BY) 操作,以下场景优先考虑建索引:
-
WHERE 子句中频繁出现的条件字段
例如:
WHERE user_id = 123
或WHERE status = 'active'
,对user_id
或status
建立索引可快速定位符合条件的行。 -
JOIN 关联的字段
多表连接时,关联字段(如
user.id
和order.user_id
)需建立索引,避免表连接时的全表扫描。 -
ORDER BY/GROUP BY 的字段
排序或分组操作若基于某字段(如
ORDER BY create_time
),索引可避免 MySQL 的临时表排序(Using filesort)。 -
高频查询的返回字段(覆盖索引)
若查询仅需返回少量字段(如
SELECT id, name FROM user WHERE age > 30
),可建立包含这些字段的复合索引(如(age, id, name)
),实现 “索引覆盖”,避免回表查询数据。
如何调整索引适应业务查询
1. 优先选择高选择性字段
这里的选择性,也叫列的区分度(或者散列度,有的叫列的基础),总之,意思指的是,这个字段内的不重复数据行数占总数据行数的比例。(选择性 = 不重复值数量 / 总记录数)。选择性越高,索引过滤效果越好。
-
user_id
(几乎唯一,选择性接近 1)比gender
(仅男 / 女,选择性低)更适合建索引。 - 一个表有10w 数据,status 字段只有三个值,1/2/3,那么这个选择性(或散列度)就很低,不适合建索引。如果这个查询很频繁,又确实需要建索引怎么办?那这时候最优选择是建复合索引。
复合索引就是多个字段共同建立的一个索引。复合索引中,应将选择性高的字段放在前面(遵循 “最左前缀匹配原则”)。select id, name from user where create_time > xxx and status=2
,就可以建立 索引Key(create_time, status)
,create_time 的可选择性几乎为1,所以它作为联合索引的第一个值,status 因为区分度很小,所以作为第二个值。
2. 复合索引的顺序至关重要
复合索引(多字段组合)遵循 “最左前缀匹配原则”:MySQL 会从左到右匹配索引字段,若左边字段未被使用,后续字段无法触发索引。
- 例:索引
(a, b, c)
可匹配WHERE a=?
、WHERE a=? AND b=?
、WHERE a=? AND b=? AND c=?
,但无法匹配WHERE b=?
或WHERE b=? AND c=?
。 - 设计建议:按 “查询频率→选择性” 排序,高频出现的字段放左,同频时选择性高的放左。
上节的sql 语句(select id, name from user where create_time > xxx and status=2
)中,where 条件,由于是先写的 create_time 然后 status ,所以它跟我们的索引天然适配。但是,如果你写的sql 为 where status=2 and create_time > xxx
,就不会走刚才建立的联合索引 Key(create_time, status)
。
这就是最左匹配原则。要求sql 中条件顺序,需要跟 索引的顺序保持一致。
如果 sql 中的条件只有 where create_time > xxx
还会走联合索引吗,答案是肯定的。也就是说联合索引,只要复合最左优先原则即可匹配索引,不需要完全匹配(所有条件必须都存在)。
复合索引可以提升索引的覆盖度(减少回表查询),相比建立多个单个字段索引降低了存储空间。提升组合查询效率。
3. 控制索引数量,避免过度索引
索引会消耗存储空间,且会降低插入、更新、删除的性能(因为每次写操作需同步维护索引)。
- 建议:单表索引不超过 5-8 个,删除长期未使用的索引(可通过
sys.schema_unused_indexes
视图查看)。
4. 避免对 “低价值” 字段建索引
以下场景不适合建索引:
- 表数据量极小(如几百行):全表扫描比索引查询更快。
- 频繁更新的字段:索引会增加更新成本(如用户的
last_login_time
频繁变动)。 - 低选择性字段(如
status
只有 2 个值):索引过滤效果差,MySQL 可能直接走全表扫描。
5. 合理设计字符串索引的长度
对长字符串(如varchar(255)
)建索引时,可指定前缀长度(如INDEX idx_name (username(10))
),减少索引占用空间。
- 原则:前缀长度需足够区分大部分值(可通过
SELECT COUNT(DISTINCT LEFT(username, 10)) / COUNT(*)
判断选择性)。
由于此部分内容较多,以上是关于 Mysql 查询优化的部分内容,后续相关内容,如何选择单字段索引还是复合索引,单字段索引和复合索引的优缺点是什么,这部分内容请关注后续文章更新。文章每日更新。欢迎常来。