答案是:可能会锁表,但取决于MySQL版本、索引类型和操作方式。
1. MySQL不同版本的区别
MySQL 5.6及之前版本
会锁表(多数情况下)
-
创建索引时会对表加上排他锁(X锁)
-
期间表不可读写,直到索引创建完成
-
对生产环境影响较大
MySQL 5.6及之后版本(Online DDL)
通常不锁表,但仍有短暂锁定
-
支持Online DDL(在线数据定义语言)
-
创建二级索引时,允许DML操作(INSERT、UPDATE、DELETE)
-
但开始和结束时有短暂元数据锁

2. 不同索引创建方式的锁表情况
创建普通二级索引(最常见场景)
sql
-- MySQL 5.6+ 通常不锁表
CREATE INDEX idx_name ON users(name);
锁表情况:
-
开始阶段:获取元数据锁(MDL),非常短暂(毫秒级)
-
创建阶段:允许DML操作,不阻塞读写
-
结束阶段:再次获取元数据锁,更新表定义
创建主键索引或改变主键
sql
-- 可能锁表,尤其是表已经有数据时
ALTER TABLE users ADD PRIMARY KEY (id);
创建全文索引或空间索引
sql
-- 通常需要锁表
CREATE FULLTEXT INDEX idx_content ON articles(content);
3. Online DDL的具体行为
支持的Online DDL操作(通常不锁表)
sql
-- 1. 添加二级索引
ALTER TABLE users ADD INDEX idx_email(email);
-- 2. 删除索引
ALTER TABLE users DROP INDEX idx_email;
-- 3. 重命名索引
ALTER TABLE users RENAME INDEX old_name TO new_name;
-- 4. 修改索引类型(如改为HASH)
ALTER TABLE users DROP INDEX idx_email, ADD INDEX idx_email USING HASH(email);
可能需要锁表的操作
sql
-- 1. 修改主键
ALTER TABLE users DROP PRIMARY KEY, ADD PRIMARY KEY(new_id);
-- 2. 修改列数据类型
ALTER TABLE users MODIFY COLUMN name VARCHAR(100);
-- 3. 添加自增列
ALTER TABLE users ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY;
-- 4. 添加/删除外键约束
ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(id);
4. 查看DDL操作的锁机制
查看DDL操作是否支持Online
sql
-- 查看支持的算法和锁类型
SHOW CREATE TABLE users\G
-- 查看具体的DDL操作信息
SELECT * FROM information_schema.INNODB_TRX
WHERE trx_state = 'LOCK WAIT';
-- 或者使用performance_schema监控
SELECT * FROM performance_schema.metadata_locks;
使用INPLACE和COPY算法对比
sql
-- 使用INPLACE算法(尽量减少锁表)
ALTER TABLE users ADD INDEX idx_phone(phone) ALGORITHM=INPLACE;
-- 使用COPY算法(会锁表)
ALTER TABLE users ADD INDEX idx_phone(phone) ALGORITHM=COPY;
5. 实际案例和最佳实践
案例1:安全添加索引(推荐)
sql
-- 1. 首先在测试环境验证
-- 2. 选择业务低峰期执行
-- 3. 监控进程状态
-- 使用INPLACE算法,指定不锁表
ALTER TABLE large_table
ADD INDEX idx_create_time(create_time),
ALGORITHM=INPLACE,
LOCK=NONE;
案例2:大表添加索引的优化
sql
-- 对于超大表,可以采用pt-online-schema-change工具
-- 而不是直接执行ALTER TABLE
-- 使用pt-online-schema-change(Percona Toolkit)
pt-online-schema-change \
--alter "ADD INDEX idx_email(email)" \
D=database,t=users \
--execute
案例3:监控DDL执行进度
sql
-- 在MySQL 5.7+中可以监控进度
SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED,
(WORK_COMPLETED/WORK_ESTIMATED)*100 as progress_pct
FROM performance_schema.events_stages_current
WHERE EVENT_NAME LIKE '%stage/innodb/alter%';
6. 不同锁级别的影响
sql
-- LOCK=NONE: 允许读写,不阻塞任何操作
ALTER TABLE users ADD INDEX idx_name(name) LOCK=NONE;
-- LOCK=SHARED: 允许读,阻塞写
ALTER TABLE users ADD INDEX idx_name(name) LOCK=SHARED;
-- LOCK=EXCLUSIVE: 阻塞读写(全表锁)
ALTER TABLE users ADD INDEX idx_name(name) LOCK=EXCLUSIVE;
7. 生产环境最佳实践
1. 评估影响
sql
-- 先检查表大小和当前负载
SELECT
TABLE_NAME,
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS 'Size(MB)',
TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db'
AND TABLE_NAME = 'your_table';
2. 使用合适的工具
-
小表 :直接使用
ALTER TABLE ... ALGORITHM=INPLACE -
大表 :使用
pt-online-schema-change或gh-ost -
云数据库:使用云服务商提供的在线DDL功能
3. 执行步骤
sql
# 1. 备份表结构
mysqldump -d your_db your_table > table_structure.sql
# 2. 测试环境验证
# 3. 业务低峰期执行
# 4. 监控性能影响
# 5. 验证索引效果
4. 避免的陷阱
sql
-- 错误做法:在事务中执行DDL
START TRANSACTION;
-- 其他DML操作...
ALTER TABLE users ADD INDEX idx_name(name); -- 可能导致长时间锁表
COMMIT;
-- 正确做法:单独执行DDL
ALTER TABLE users ADD INDEX idx_name(name);
8. 常见问题解答
Q: Online DDL真的完全不锁表吗?
A: 不完全。Online DDL在开始和结束时需要获取元数据锁,虽然非常短暂(毫秒到秒级),但如果有长时间未提交的事务,可能会导致等待。
Q: 如何知道DDL操作是否在执行中?
sql
-- 查看当前运行进程
SHOW PROCESSLIST;
-- 或者使用sys库(MySQL 5.7+)
SELECT * FROM sys.session WHERE command = 'Query';
Q: 添加索引失败会怎样?
A: MySQL会回滚操作,表会恢复到之前状态,但期间可能消耗了大量系统资源。
9. 总结
| 场景 | 是否锁表 | 建议 |
|---|---|---|
| MySQL 5.6+,添加二级索引 | 基本不锁表 | 使用ALGORITHM=INPLACE, LOCK=NONE |
| 修改主键或列类型 | 通常锁表 | 使用pt-online-schema-change |
| 大表添加索引 | 可能长时间锁表 | 使用gh-ost或分批操作 |
| 生产环境高峰期 | 尽量不操作 | 选择业务低峰期 |
最终建议:
-
MySQL 5.6+版本:添加普通二级索引通常不锁表,可放心使用
-
主键操作或列修改:需要谨慎,可能锁表
-
超大表操作:使用专业工具(pt-online-schema-change、gh-ost)
-
生产环境:先在测试环境验证,选择合适时间执行
-
监控:执行时监控数据库性能和锁状态
在大多数现代MySQL部署中(5.6+),正确使用Online DDL可以实现在不锁表的情况下添加索引,对业务影响极小。