mysql查询性能优化的核心之一是合理优化where条件,让查询能够充分利用已有索引,减少全表扫描的概率。索引是mysql提升查询效率的关键数据结构,只有where条件符合索引的匹配规则,才能发挥索引的作用。

mysql索引的基本匹配规则
要优化where条件,首先需要了解mysql索引的匹配逻辑,常见的B+树索引遵循最左前缀匹配原则,同时有一些固定的失效场景需要规避。
最左前缀匹配原则
对于联合索引(col1, col2, col3),索引会先按照col1排序,col1相同再按col2排序,以此类推。因此查询条件必须包含联合索引的最左列,才能匹配到该索引。比如索引是(a, b, c),以下条件可以匹配索引:
- where a = 1
- where a = 1 and b = 2
- where a = 1 and b = 2 and c = 3
而以下条件无法匹配该联合索引:
- where b = 2
- where a = 1 and c = 3
常见索引失效场景
即使where条件包含了索引列,也可能因为写法问题导致索引失效,常见情况包括:
- 对索引列进行函数运算、算术运算,比如
where year(create_time) = 2023 - 索引列参与隐式类型转换,比如索引列是varchar类型,条件写成
where phone = 13800138000 - 使用
like以通配符开头,比如where name like '%张三' - 使用
or连接条件,且其中一个条件列没有索引 - 使用
!=、<>、is null、is not null等操作符(部分存储引擎下可能失效)
where条件优化核心方法
调整条件顺序匹配最左前缀
对于联合索引,where条件的列顺序不影响匹配结果,但建议按照联合索引的定义顺序编写条件,可读性更高,也避免后续索引调整时出现问题。比如联合索引是(user_id, status, create_time),查询条件可以写成:
-- 符合最左前缀,能利用索引 select * from orders where user_id = 1001 and status = 1 and create_time >= '2023-01-01'; -- 调整顺序后依然可以匹配索引,但可读性稍差 select * from orders where status = 1 and user_id = 1001 and create_time >= '2023-01-01';
避免索引列参与运算和函数
如果需要对索引列做处理,尽量把运算放到条件的值一侧,而不是索引列一侧。比如需要查询创建时间在2023年的订单,错误写法和正确写法对比如下:
-- 错误写法:对索引列使用函数,索引失效 select * from orders where year(create_time) = 2023; -- 正确写法:运算放在值侧,索引可以生效 select * from orders where create_time >= '2023-01-01' and create_time < '2024-01-01';
利用覆盖索引减少回表
如果查询的字段全部包含在索引中,mysql不需要回表查询聚簇索引的数据,能进一步提升查询效率。比如有联合索引(user_id, status),查询只需要user_id和status两个字段时,可以写成:
-- 覆盖索引,不需要回表 select user_id, status from orders where user_id = 1001 and status = 1; -- 查询所有字段,需要回表,效率更低 select * from orders where user_id = 1001 and status = 1;
合理使用范围查询
联合索引中,范围查询(>、<、between等)的列之后的索引列无法继续使用索引。比如联合索引是(a, b, c),条件where a = 1 and b > 2 and c = 3中,只有a和b能用到索引,c无法使用。因此设计联合索引时,建议把范围查询的列放在索引的最后一位。
索引利用效果验证
可以通过explain命令查看查询语句的执行计划,判断where条件是否利用了索引。执行计划中的key字段表示实际使用的索引,type字段表示访问类型,ref、range等都是较好的索引利用类型,ALL表示全表扫描。
示例验证如下:
-- 查看执行计划 explain select * from orders where user_id = 1001 and status = 1;
如果key字段显示对应的联合索引名称,说明where条件成功利用了索引,否则需要检查条件写法是否符合索引匹配规则。