普通索引适用于where条件中频繁出现但不要求唯一性的字段,如status、category、created_at;需避免在text或超长varchar上直接建索引,联合索引须遵循最左前缀原则,且不可在主键或已有唯一约束字段上冗余创建。

直接加 CREATE INDEX 就能生效,但加错字段、加错顺序或忽略写负载,反而让系统更慢。
什么时候该用普通索引(非唯一、非主键)
普通索引适用于:WHERE 条件里频繁出现、但不要求唯一性的字段,比如 status、category、created_at。它不阻止重复值,也不影响主键逻辑,是优化查询最常用的索引类型。
- 用户表中查
SELECT * FROM users WHERE status = 'active'——status就适合建普通索引 - 订单表中按时间范围筛选:
WHERE created_at BETWEEN '2026-01-01' AND '2026-06-30'——created_at也适合 - 别在
TEXT或超长VARCHAR字段上直接建普通索引,会浪费空间;可指定前缀长度,如name(10) - 如果字段本身是主键或已有唯一约束,再建普通索引纯属冗余,MySQL 会忽略或报错
CREATE INDEX 语句怎么写才安全
最简形式是 CREATE INDEX idx_status ON users(status);,但要注意三点:
- 索引名建议带表名和字段名前缀,避免跨表重名,比如
idx_users_status而不是idx_status - 多个字段联合索引必须考虑顺序:WHERE 中先出现的字段放前面,例如
WHERE category = ? AND status = ?,应建CREATE INDEX idx_category_status ON products(category, status);反过来就可能失效 - 线上加索引会锁表(尤其 MyISAM),InnoDB 从 5.6+ 支持
ALGORITHM=INPLACE,但仍有短暂阻塞;生产环境务必在低峰期执行,或用pt-online-schema-change工具
普通索引为什么有时不生效
常见失效场景不是语法错,而是查询写法或数据分布触发了 MySQL 的“放弃使用索引”逻辑:
- 对索引字段做函数操作:
WHERE YEAR(created_at) = 2026→ 改成WHERE created_at >= '2026-01-01' AND created_at - 隐式类型转换:
status是TINYINT,却写成WHERE status = '1'→ 改为WHERE status = 1 - LIKE 以通配符开头:
WHERE name LIKE '%三'无法走索引;WHERE name LIKE '张%'可以 - 索引选择性太低:比如
gender只有 'M'/'F' 两个值,MySQL 可能直接全表扫描——这时加索引意义不大
加完索引后必须验证效果
不能只看 SQL 是否“跑通”,要确认是否真走了索引:
- 用
EXPLAIN SELECT * FROM users WHERE status = 'active';查看type是否为ref或range,key是否显示你刚建的索引名 - 注意
rows值:从几十万降到几百,才算有效;如果还是显示全表行数,说明没用上 - 观察写入性能:插入/更新变慢超过 10%,就要检查是否索引过多,或字段更新太频繁(索引需同步维护)
- 定期用
SHOW INDEX FROM users;确认索引存在且未被自动删除(某些运维脚本或误操作可能删掉)
真正难的不是建索引的动作,而是判断“这个字段值的分布、这个查询的写法、这个业务的读写比例”是否值得加——很多慢查询背后,其实该优化的是 SQL 逻辑或分页方式,而不是盲目堆索引。











