直接加普通索引用 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模式来看,而不是只记语法。
