在MySQL日常运维中,修改数据表结构是非常常见的操作,比如新增字段、修改字段类型、添加索引等。但有时候执行ALTER TABLE语句时会长时间无响应,或者返回错误提示,这类问题大多由磁盘空间不足或元数据锁冲突导致,下面分别介绍对应的排查和解决方法。

一、检查磁盘空间是否充足
MySQL修改表结构时,尤其是涉及大表变更时,需要额外的磁盘空间存储临时数据,如果磁盘空间不足,操作会直接失败或者卡住。可以通过以下步骤检查磁盘空间:
1. 查看服务器磁盘使用情况
如果是Linux服务器,可以直接执行df命令查看磁盘占用:
# 查看所有挂载点的磁盘使用情况,人类可读格式显示 df -h
重点关注MySQL数据目录所在的挂载点,一般默认路径是/var/lib/mysql,如果剩余空间低于表大小的1.5倍,就可能出现空间不足的问题。
2. 查看MySQL临时目录空间
MySQL执行ALTER TABLE时可能会使用临时目录存储中间数据,可以通过SQL查看临时目录配置:
-- 查看MySQL临时文件目录配置 SHOW VARIABLES LIKE 'tmpdir';
如果临时目录所在磁盘空间不足,也会导致表结构修改失败,需要清理对应目录的无用文件,或者调整tmpdir参数指向空间充足的路径。
二、排查元数据锁冲突
元数据锁(Metadata Lock,简称MDL)是MySQL用来保护表元数据一致性的锁,当对表进行增删改查操作时,会自动加MDL读锁,而修改表结构需要加MDL写锁,如果此时有其他事务持有该表的MDL读锁,写锁就会被阻塞,导致ALTER TABLE操作卡住。
1. 查看当前阻塞的MDL锁
MySQL 5.7及以上版本可以通过performance_schema库查看MDL锁的等待情况,执行以下SQL:
-- 查看当前MDL锁等待情况,需要开启performance_schema的相关监控
SELECT
p.OBJECT_SCHEMA AS 库名,
p.OBJECT_NAME AS 表名,
p.LOCK_TYPE AS 锁类型,
p.LOCK_STATUS AS 锁状态,
p.SOURCE AS 锁来源,
t.PROCESSLIST_ID AS 线程ID,
t.PROCESSLIST_INFO AS 执行SQL
FROM performance_schema.metadata_locks p
JOIN performance_schema.threads t ON p.OWNER_THREAD_ID = t.THREAD_ID
WHERE p.OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'sys')
AND p.LOCK_STATUS = 'PENDING';
如果查询结果中有对应表的PENDING状态的写锁,说明存在MDL锁冲突。
2. 解决MDL锁冲突
找到阻塞的源头后,可以根据实际情况处理:
- 如果是长事务持有MDL读锁,可以等待事务执行完成,或者kill掉对应的线程,执行KILL 线程ID;即可。
- 如果是程序频繁查询该表导致一直有读锁,可以在业务低峰期执行表结构修改,或者先暂停对应业务的查询请求。
三、修改表结构的注意事项
为了避免修改表结构时出现问题,建议遵循以下规范:
- 大表结构修改优先选择业务低峰期执行,避免影响正常业务。
- 修改前先备份表结构和数据,防止操作失败导致数据丢失。
- 如果是MySQL 5.6及以上版本,可以开启Online DDL,减少锁表时间,执行ALTER TABLE时加上ALGORITHM=INPLACE, LOCK=NONE参数(部分操作不支持该参数,需要根据实际情况调整)。
- 修改前先检查磁盘空间和当前表的活跃事务,排除潜在问题再执行操作。
以上就是MySQL无法修改数据表结构时,检查磁盘空间与元数据锁的完整方法,按照步骤排查基本可以解决大部分表结构修改失败的问题。