create index最简单,alter table适合批量操作;需注意索引名唯一、长文本指定前缀、避免锁表、验证索引生效及联合索引最左前缀原则。

直接加普通索引用 CREATE INDEX 最简单
对已有表快速加单列普通索引,CREATE INDEX 是最直观的方式。它不改变表结构,只新增索引对象,语法轻量、意图明确。
常见错误是漏写索引名或误用主键语法(比如加了 PRIMARY KEY 或 UNIQUE 关键字),结果建成了唯一索引或主键——普通索引不需要这些修饰。
- 基本写法:
CREATE INDEX idx_user_name ON users (name); - 多列索引(联合索引):
CREATE INDEX idx_user_status_created ON users (status, created_at); - 注意:索引名(如
idx_user_name)必须唯一,且不能和已有索引重名,否则报错ERROR 1061 (42000): Duplicate key name - 如果列类型是长文本(如
TEXT),需指定前缀长度:CREATE INDEX idx_post_title ON posts (title(100));
ALTER TABLE ... ADD INDEX 更适合批量操作
当你要同时加索引、改字段、调默认值时,ALTER TABLE 一条命令搞定更高效。MySQL 会合并为一次表变更,比单独执行多次 CREATE INDEX 减少锁表时间(尤其在大表上)。
但要注意:5.7+ 版本默认使用 ALGORITHM=INPLACE,可避免全表拷贝;而旧版本或某些复杂场景仍可能触发 COPY 算法,导致长时间锁表。
- 单列索引:
ALTER TABLE orders ADD INDEX idx_order_user_id (user_id); - 联合索引 + 同时加字段:
ALTER TABLE logs ADD COLUMN trace_id VARCHAR(32), ADD INDEX idx_logs_trace_time (trace_id, created_at); - 若想静默跳过已存在的索引(避免报错),MySQL 不支持
IF NOT EXISTS于ADD INDEX,得先查information_schema.STATISTICS或用脚本判断
别忽略 ONLINE DDL 和锁表现象
即使只是加普通索引,MySQL 在低版本或配置不当的情况下仍可能对表加 SHARED 或 EXCLUSIVE 锁,导致写入阻塞。这不是语法问题,而是执行期行为。
典型表现:执行 CREATE INDEX 后,INSERT/UPDATE 卡住,SHOW PROCESSLIST 显示状态为 waiting for table metadata lock。
- 5.6.17+ 支持
ALGORITHM=INPLACE, LOCK=NONE(仅限某些存储引擎和索引类型),可显式指定:ALTER TABLE t ADD INDEX idx_x (x), ALGORITHM=INPLACE, LOCK=NONE; - 但
LOCK=NONE并非总可用:若表含全文索引、或使用 MyISAM 引擎,则自动降级为LOCK=SHARED - 生产环境加索引前,建议先在从库或低峰期测试执行时间与锁表现象,而不是只看语法是否通过
建完记得验证索引是否生效
语法成功不代表查询真走索引。常见误区是建了索引却因查询条件没覆盖最左前缀、或隐式类型转换导致索引失效。
最可靠方式是用 EXPLAIN 看执行计划,重点关注 key 和 possible_keys 字段是否命中你刚建的索引名。
- 检查索引是否存在:
SHOW INDEX FROM users WHERE Key_name = 'idx_user_name'; - 验证是否被用到:
EXPLAIN SELECT * FROM users WHERE name = 'alice';—— 若key列显示idx_user_name,才算真正生效 - 联合索引要注意顺序:查
(status, created_at)时,WHERE created_at > '2023-01-01'单独出现不会走索引;必须带上status条件才可能触发
索引不是建完就一劳永逸,真正难的是判断“该不该建”“建在哪一列”“要不要加前缀”——这些都得结合慢查日志和实际 WHERE 模式来看,而不是只记语法。










