索引是数据库的“目录页”,能大幅提升查询效率,但需根据查询条件和执行计划合理创建,避免滥用导致写入性能下降和优化器误判。

索引就是数据库的“目录页”,不建它,查得越慢,表越大越明显
MySQL 默认不给任何字段建索引——就像一本没目录的 1000 页书,你要找“事务隔离级别”这个词,只能一页页翻。建了索引,相当于提前把关键词按顺序整理好,再附上对应页码(即数据行位置),SELECT 时直接跳转,省掉全表扫描(type: ALL)的 IO 开销。实际中,10 万行以上的表,一个没索引的 WHERE user_id = 12345 可能要扫几万行;加了索引后,通常只需 3–4 次磁盘 IO 就定位到数据。
什么时候建索引?看查询条件和执行计划,不是看“字段重不重要”
建索引不是凭感觉,而是看 EXPLAIN 输出里哪些列反复出现在 WHERE、ORDER BY、GROUP BY 或 JOIN 条件中。常见有效场景包括:
-
WHERE status = 'active'—— 但前提是该字段区分度高(比如 90% 是 active 就别建) -
ORDER BY created_at DESC LIMIT 20—— 索引能避免排序临时表 -
JOIN orders ON users.id = orders.user_id—— 关联字段必须有索引,否则驱动表每扫一行,被驱动表都全表扫一遍 - 组合查询如
WHERE category = 'book' AND price BETWEEN 20 AND 50—— 考虑建联合索引(category, price),注意最左前缀原则
为什么有时候建了索引也不走?这些操作一写就失效
索引不是建了就自动生效。MySQL 优化器会权衡成本,遇到以下情况,常直接放弃索引,退化为全表扫描:
- 对索引列用函数:
WHERE YEAR(create_time) = 2025→ 改成WHERE create_time >= '2025-01-01' AND create_time -
隐式类型转换:
user_id是VARCHAR,却写WHERE user_id = 12345→ MySQL 自动转成数字比较,索引失效 - 前导通配符:
WHERE name LIKE '%john'→ B+Tree 无法从左边开始匹配,LIKE 'john%'才能用索引 - OR 连接非索引字段:
WHERE a = 1 OR b = 2,若只有a有索引,整个条件大概率不用索引 - 索引列参与计算:
WHERE score * 2 > 100→ 改成WHERE score > 50
建索引不是免费的,写多读少的表要格外谨慎
每次 INSERT、UPDATE、DELETE 都要同步更新所有相关索引树,尤其在高并发写入场景下,索引越多,写延迟越明显,甚至引发锁竞争。同时:
- 每个索引都占磁盘空间,
VARCHAR(255)字段建索引,可能比原数据还大 - 过多索引会让优化器选错执行计划,因为要花时间评估哪个索引“更优”
- 主键索引(聚簇索引)决定数据物理存储顺序,
INNODB表只能有一个;其他都是二级索引,查完还要回表取完整行
真正该建索引的地方,是那些被高频查询、且过滤性足够强的字段——不是所有 WHERE 都值得加,更不是所有字段都适合单独建索引。联合索引的设计、字段顺序、是否覆盖查询,才是进阶关键。










