安全写法是CREATE INDEX必须明确指定ON table_name(col1,col2...)且按高频查询条件排顺序,禁用无ALGORITHM=INPLACE和LOCK=NONE的ALTER TABLE ADD INDEX,执行前需核对索引名、引擎、字符集并验证。
MySQL 多列联合索引 DDL 语句怎么写才安全
直接上结论:create index 必须带明确的 on table_name (col1, col2, ...),且列顺序不能凭感觉排——它直接影响查询能否命中索引。生产环境严禁用 alter table ... add index 不加 algorithm=inplace 和 lock=none(mysql 5.6+),否则会锁表。
常见错误现象:ALTER TABLE t ADD INDEX idx_a_b (a,b) 在大表上执行时,应用突然卡住、超时;或者加完索引后 WHERE b = ? 查询依然走全表扫描——因为没理解最左前缀原则。
-
a和b的顺序取决于高频查询条件:如果常查WHERE a = ? AND b = ?,优先(a,b);如果还有WHERE a = ? ORDER BY b,也适合这个顺序 - 避免把低选择性列(如
status TINYINT只有 0/1)放在最左:索引区分度太低,优化器可能直接放弃使用 - MySQL 8.0+ 支持降序索引,但
ORDER BY a ASC, b DESC需显式声明(a ASC, b DESC),否则仍可能不走索引
生成 DDL 脚本前必须确认的三件事
DDL 脚本不是复制粘贴就完事。很多线上事故源于“脚本生成了,但没核对上下文”。
- 确认目标表当前是否有同名索引:
SHOW INDEX FROM table_name,避免Duplicate key name报错中断执行 - 检查表引擎:MyISAM 不支持
ALGORITHM=INPLACE,必须切到 InnoDB;否则 DDL 会退化为拷表,耗时指数级增长 - 验证字符集和排序规则一致性:如果列是
VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs,而新建索引没指定,MySQL 可能隐式转换导致无法命中
在生产环境执行前怎么最小化风险
核心原则:不直接在主库上跑 CREATE INDEX,尤其数据量 > 100 万行。
- 先在从库或影子库上执行,用
EXPLAIN FORMAT=tree验证新索引是否被实际选用 - 用
pt-online-schema-change(Percona Toolkit)做无锁变更:它自动拆分 DDL 为小批次同步,但要求表必须有主键或唯一非空索引 - 如果必须原生 DDL,务必加约束:
ALTER TABLE t ADD INDEX idx_x_y (x,y), ALGORITHM=INPLACE, LOCK=NONE;—— 缺少任一参数,MySQL 都可能 fallback 到阻塞模式
为什么有些联合索引建了等于没建
不是语法错,而是语义失效。最常被忽略的是隐式类型转换和函数包裹。
-
WHERE DATE(create_time) = '2024-01-01':哪怕create_time上有索引,也会因DATE()函数导致索引失效 -
WHERE user_id = '123':如果user_id是INT类型,字符串比较会触发隐式转换,索引可能失效(尤其 MySQL 8.0 严格模式下) - 联合索引
(a,b,c)对WHERE a = ? AND c = ?无效——跳过b就断了最左前缀,只能用到a单列部分
复杂点在于:这些失效往往只在特定数据分布或查询条件下暴露,上线后流量一上来才出问题。所以 DDL 前的 EXPLAIN 必须用真实参数值,而不是 ? 占位符。

















