索引并非万能,查询慢常见原因包括WHERE未用索引字段、函数/类型转换致失效、未加LIMIT返回大量行;需用EXPLAIN分析执行计划,遵循最左前缀等原则,并权衡读写性能。

为什么加了索引查询还是慢
索引不是万能的,常见原因是 WHERE 条件没用上索引字段、用了函数或类型转换导致索引失效、或者查询返回大量行却没加 LIMIT。比如 SELECT * FROM users WHERE YEAR(create_time) = 2023,YEAR() 会让 create_time 上的索引完全失效。
实操建议:
- 用
EXPLAIN看执行计划,重点检查type(是否为range/ref)、key(实际用了哪个索引)、rows(预估扫描行数) - 避免在索引列上做计算、隐式类型转换(如字符串字段跟数字比较)、
LIKE '%abc'开头通配 - 复合索引要遵循最左前缀原则:索引
(a, b, c)能用于WHERE a=1 AND b=2,但不能用于WHERE b=2 AND c=3
哪些字段适合建索引
高频出现在 WHERE、JOIN ON、ORDER BY、GROUP BY 中的字段优先考虑,但不是越多越好——索引会拖慢写入,且占用磁盘和内存。
实操建议:
- 主键自动有聚簇索引,不用额外建
- 外键字段建议加索引,否则
JOIN或DELETE CASCADE可能锁表 - 区分度高的字段(如
email)比低区分度字段(如status只有 0/1)更适合单列索引 - 把多个常一起出现的过滤条件合并成一个复合索引,比如经常查
WHERE category_id = ? AND is_deleted = 0 ORDER BY created_at DESC,就建(category_id, is_deleted, created_at)
如何安全地添加索引不锁表
MySQL 5.6+ 支持 ALGORITHM=INPLACE,但不是所有操作都免锁。比如给大表加普通索引,在 5.6–5.7 默认仍会锁表;8.0 起多数 DDL 才真正支持在线加索引。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
实操建议:
- 生产环境加索引前,先在从库或影子库验证执行时间和资源消耗
- 用
ALTER TABLE t ADD INDEX idx_name (col) ALGORITHM=INPLACE, LOCK=NONE;显式声明,如果报错说明不支持,别硬上 - 对超大表(千万级以上),考虑用
pt-online-schema-change工具,它通过触发器双写实现无锁变更 - 注意
innodb_online_alter_log_max_size配置,太小会导致在线 DDL 中途失败
索引太多反而让查询变慢
优化器在几十个索引里选错执行计划的概率会上升,而且每个索引都要维护 B+ 树结构,INSERT/UPDATE/DELETE 时开销翻倍。有些“看起来有用”的索引其实从没被用过。
实操建议:
- 定期查
information_schema.STATISTICS或用sys.schema_unused_indexes(MySQL 8.0+)识别长期未使用的索引 - 删除重复索引:比如已有
(a, b),再建(a)就是冗余 - 监控
Handler_read_next和Handler_read_rnd_next,后者值高说明排序/临时表多,可能需要调整索引覆盖字段 - 不要迷信“覆盖索引”就一定好——如果只为了覆盖几个小字段而把索引扩大到几 MB,缓存效率反而下降
索引优化本质是权衡:读快一点,还是写稳一点;查得准一点,还是空间省一点。没有一劳永逸的方案,只有持续观察 EXPLAIN 和慢日志里的真实行为。










