MySQL优化器自动重排WHERE条件顺序,真正影响性能的是索引设计、是否覆盖、ICP支持及避免对索引字段使用函数或运算。
MySQL 优化器会自动重排 WHERE 子句中的条件顺序,按索引选择性、数据分布、成本估算重新决定执行路径。你写成 WHERE status = 'active' AND created_at > '2025-01-01' 还是反过来,对执行计划没有实质影响——除非你用的是非常老的 MySQL 5.5 且关闭了优化器(几乎不存在)。
真正起作用的是:字段是否在索引中、索引是否覆盖、条件是否能触发索引下推(ICP)、是否用了函数或表达式导致索引失效。
EXPLAIN 看 key 和 possible_keys,而不是靠肉眼猜顺序type 是 ALL 或 index,说明没走有效索引,该看的是索引设计,不是 WHERE 写法
对索引字段做函数调用(如 DATE(created_at))、类型转换(如 CAST(user_id AS CHAR))、数学运算(如 price * 1.1 > 100),会让 MySQL 无法使用该字段上的 B+ 树索引,只能全表扫描或索引全扫。
常见错误写法:
SELECT * FROM orders WHERE YEAR(created_at) = 2025;
正确替代方式:
created_at >= '2025-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%' 可以走索引前缀WHERE id IN (1,2,3,...,2000) 看似简单,但当值超过几百个时,MySQL 会退化为多个等值查找合并,优化器可能放弃使用索引、改走全表扫描,尤其在旧版本(如 5.6)中更明显。
实操建议:
IN 值数控制在 200 以内,超量拆成多批次查询JOIN 替代 IN (SELECT ...),后者容易触发临时表和文件排序IN 子查询的物化优化,但仍有阈值,仍建议用 JOIN 更可控CREATE TEMPORARY TABLE tmp_ids(id BIGINT PRIMARY KEY); INSERT INTO tmp_ids VALUES (...); SELECT * FROM t JOIN tmp_ids USING(id);
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
NOT NULL 并加约束,让优化器更敢用索引EXPLAIN 中的 rows 是否异常高——可能正在扫描大量 NULL 项最常被忽略的一点:复合索引中只要有一个字段允许 NULL,且查询条件跳过它(比如用 WHERE a = 1 AND c = 3,跳过 b),而 b 是可空字段,某些旧版本可能无法高效利用后续字段的索引下推。别只盯着 WHERE 怎么写,先确认表结构和索引定义是否干净。