MySQL索引生效严格依赖查询条件的字段、顺序、操作符和数据类型;强行建覆盖所有字段的联合索引既不满足最左前缀原则,又因索引过大拖慢写入和缓存效率。

没有“通用索引”这种东西。MySQL 的索引生效严格依赖查询条件的字段、顺序、操作符和数据类型,所谓“动态多条件”如果每次 WHERE 子句字段组合都不同,强行建一个覆盖所有字段的联合索引,反而会让绝大多数查询完全用不上——它既不满足最左前缀,又因索引过大拖慢写入和缓存效率。
为什么WHERE a = ? AND b > ? AND c IN (?, ?)不能靠一个INDEX(a,b,c)通吃
复合索引 INDEX(a,b,c) 只能高效支持以下模式:
-
a = ? AND b = ? AND c = ?(全等值) -
a = ? AND b > ?(等值 + 紧跟其后的范围) -
a = ? AND b BETWEEN ? AND ? AND c = ?(等值 + 范围 + 等值?不行!c 在范围之后,无法走索引)
一旦出现 c IN (...) 且 c 不在最左,或 b > ? 后还带 ORDER BY c,该索引对 c 的部分就失效了。更糟的是,如果查询只用 b > ? 或 c = ? 单独出现,这个索引根本不会被选中——type 会是 ALL。
真正可行的策略:按高频固定组合建索引,而非“猜”动态条件
别试图覆盖所有排列组合。先用慢日志和 EXPLAIN 找出真实业务里 TOP 5 的 WHERE 模式,例如:
-
WHERE status = ? AND created_at > ?→ 建INDEX(status, created_at) -
WHERE user_id = ? AND updated_at > ? ORDER BY id DESC→ 建INDEX(user_id, updated_at, id)(覆盖排序,避免Using filesort) -
WHERE phone = ? OR email = ?→ 别指望单索引;拆成两个单列索引INDEX(phone)和INDEX(email),让优化器用UNION或index_merge(但注意OR在某些版本下仍可能退化)
重点看 EXPLAIN 的 key_len:如果它只显示用了前 2 字节,说明第三个字段没参与索引查找——那它就不该放进来。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
覆盖索引不是“字段越多越好”,而是“刚好够用”
比如常查 SELECT id, username, status FROM users WHERE status = ? AND created_at > ?,与其建 INDEX(status, created_at) 再回表,不如建 INDEX(status, created_at, id, username, status) ——但注意:status 已是等值条件字段,重复包含无意义;InnoDB 二级索引叶子节点自带主键 id,所以最终只需 INDEX(status, created_at, username) 即可覆盖查询,EXTRA 显示 Using index 才算成功。
别把 VARCHAR(255) 字段直接塞进索引。要么用前缀索引(INDEX(username(191))),要么确认该字段实际长度极少超 50 字符再建全量索引。
IN 查询和模糊搜索必须单独处理,别指望常规索引兜底
WHERE username LIKE '%关键词%' 或 WHERE tag_id IN (1,2,3,...200) 这类查询,常规 B+Tree 索引基本无效:
-
LIKE '%x'无法用索引;改用全文索引FULLTEXT(username)+MATCH ... AGAINST,或引入 Elasticsearch -
IN值过多(> 200)时,优化器大概率放弃索引走全表;应拆成多次小批量查询,或用临时表JOIN -
IN字段本身区分度低(如status IN (0,1)),索引选择性差,即使走了索引,rows也可能高达总行数的 50%,此时还不如全表扫描
最易被忽略的一点:千万级表上,ALTER TABLE ADD INDEX 会锁表(5.6+ 支持 ALGORITHM=INPLACE,但仍需注意磁盘 IO 和缓冲池压力)。上线前务必在从库验证执行时间,并避开业务高峰。










