mysql优化WHERE子句条件的写法与性能提升

来源:这里教程网 时间:2026-02-28 20:41:19 作者:

WHERE 条件顺序不影响 MySQL 执行计划

MySQL 优化器会自动重排

WHERE
子句中的条件顺序,按索引选择性、数据分布、成本估算重新决定执行路径。你写成
WHERE status = 'active' AND created_at > '2023-01-01'
还是反过来,对执行计划没有实质影响——除非你用的是非常老的 MySQL 5.5 且关闭了优化器(几乎不存在)。

真正起作用的是:字段是否在索引中、索引是否覆盖、条件是否能触发索引下推(ICP)、是否用了函数或表达式导致索引失效。

不要手动把“过滤性强”的条件写前面来“优化”,这是过时经验
EXPLAIN
key
possible_keys
,而不是靠肉眼猜顺序
如果发现
type
ALL
index
,说明没走有效索引,该看的是索引设计,不是 WHERE 写法

避免在 WHERE 字段上用函数或运算

对索引字段做函数调用(如

DATE(created_at)
)、类型转换(如
CAST(user_id AS CHAR)
)、数学运算(如
price * 1.1 > 100
),会让 MySQL 无法使用该字段上的 B+ 树索引,只能全表扫描或索引全扫。

常见错误写法:

SELECT * FROM orders WHERE YEAR(created_at) = 2023;

正确替代方式:

改用范围查询:
created_at >= '2023-01-01' AND created_at 
如果必须按年查且高频,可加生成列 + 索引:
ALTER TABLE orders ADD COLUMN year_created TINYINT GENERATED ALWAYS AS (YEAR(created_at)) STORED, ADD INDEX idx_year (year_created);
避免
LIKE '%abc'
,它无法利用索引;
LIKE 'abc%'
可以走索引前缀

IN 列表过大时注意性能拐点

WHERE id IN (1,2,3,...,2000)
看似简单,但当值超过几百个时,MySQL 会退化为多个等值查找合并,优化器可能放弃使用索引、改走全表扫描,尤其在旧版本(如 5.6)中更明显。

实操建议:

单次
IN
值数控制在 200 以内,超量拆成多批次查询
若来源是另一张表,优先用
JOIN
替代
IN (SELECT ...)
,后者容易触发临时表和文件排序
MySQL 8.0+ 支持
IN
子查询的物化优化,但仍有阈值,仍建议用
JOIN
更可控
考虑用临时表承载大批量 ID:
CREATE TEMPORARY TABLE tmp_ids(id BIGINT PRIMARY KEY); INSERT INTO tmp_ids VALUES (...); SELECT * FROM t JOIN tmp_ids USING(id);

NULL 判断要小心索引失效风险

WHERE col IS NULL
在有允许 NULL 的普通索引(非唯一索引)上通常可以走索引;但
WHERE col != 'x'
WHERE col  'x'
会隐式包含
NULL
,而 B+ 树索引不存储 NULL 值(除非是唯一索引的 NULL 特殊处理),这类查询往往触发全索引扫描甚至全表扫描。

典型陷阱:

SELECT * FROM users WHERE email != 'test@example.com'; -- 若 email 允许 NULL,此语句实际返回所有 email 不等于该值的行 + 所有 email IS NULL 的行

更安全写法:

明确拆开:
WHERE email != 'test@example.com' AND email IS NOT NULL
如果业务上 email 不应为 NULL,就该设为
NOT NULL
并加约束,让优化器更敢用索引
对可空字段建索引时,留意
EXPLAIN
中的
rows
是否异常高——可能正在扫描大量 NULL 项

最常被忽略的一点:复合索引中只要有一个字段允许 NULL,且查询条件跳过它(比如用

WHERE a = 1 AND c = 3
,跳过
b
),而
b
是可空字段,某些旧版本可能无法高效利用后续字段的索引下推。别只盯着 WHERE 怎么写,先确认表结构和索引定义是否干净。

相关推荐