主库ALTER TABLE导致从库卡住的根源是binlog记录方式与从库重放逻辑不匹配,即使主库使用ALGORITHM=INPLACE,binlog仍可能以COPY模式写入,使从库SQL线程重建表并加MDL锁阻塞查询。

主库直接跑ALTER TABLE为什么从库会卡住
不是因为主库锁表,而是binlog记录方式和从库重放逻辑不匹配。比如主库执行ALTER TABLE t ADD COLUMN c INT DEFAULT 0,即使用了ALGORITHM=INPLACE,binlog里仍可能以ALGORITHM=COPY形式写入——从库SQL线程一读到这个语句,就会重建整张表、加MDL锁、阻塞所有查询。
常见触发点包括:MODIFY COLUMN改长度(utf8mb4下VARCHAR从255→500)、加NOT NULL但字段存在NULL值、MySQL 8.0.23之前改默认值——这些操作在主库看似“在线”,从库却被迫降级为COPY模式。
-
binlog_row_image=MINIMAL开启时,某些DDL的row event可能缺失字段,导致从库报Last_SQL_Errno: 1785 - 主库没显式指定
LOCK=NONE,MySQL默认可能用LOCK=SHARED,从库SQL线程等MDL锁时,Seconds_Behind_Master会跳变归零再飙升 - GTID模式下,若变更中执行了
SET GLOBAL sql_log_bin = 0,从库gtid_executed无法对齐,后续同步直接中断
pt-online-schema-change必须加的三个参数
pt-osc默认只连主库,对从库完全无感。漏掉任意一个关键参数,都可能出现“主库结构已更新,从库还在用旧表”的静默故障。
-
--recursion-method=none:禁用自动探测从库拓扑,避免工具误连错节点(比如连到二级从库而非直连主库) -
--check-slave-lag=h=10.0.1.2,u=repl,p=xxx,P=3306:显式指定一个从库并持续监控延迟,一旦Seconds_Behind_Master > 30秒自动暂停拷贝,防止追不上 -
--slave-user=repl --slave-password=xxx:确保能在从库执行SELECT COUNT(*)校验数据一致性;不加的话校验跳过,你根本不知道主从是否真一致
示例命令:pt-online-schema-change D=test,t=user --alter "ADD COLUMN status TINYINT DEFAULT 0" --recursion-method=none --check-slave-lag=h=10.0.1.2,u=repl,p=xxx --slave-user=repl --slave-password=xxx --execute
GTID模式下变更前后必须验证的两个点
GTID让DDL可追溯,但也让校验更严格——不是“包含”就行,必须完全相等。
- 变更前,在主库执行
SELECT @@global.gtid_executed;,记下返回值(如aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-100) - 变更完成后,在从库执行同一条语句,结果必须和主库**一字不差**;如果从库多出或少了一段GTID,说明有事务被跳过或重复执行
特别注意:WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS函数只在MySQL 8.0.23+支持,旧版本只能靠SELECT MASTER_POS_WAIT()配合传统位点校验,且必须确认Slave_SQL_Running_State显示Waiting for dependent transaction to commit才算真正追平。
Online DDL在主从环境中的真实适用边界
别迷信ALGORITHM=INPLACE——它只保证不重建主表,不代表无锁、不阻塞从库。真正能放心用的场景其实很窄:
- MySQL 8.0.12+ 的
ALGORITHM=INSTANT:仅限ADD COLUMN(不能是首列、不能带非空默认值),速度最快、真正无锁、从库也安全 -
ADD INDEX(非唯一、非全文):主库LOCK=NONE成功,从库一般能重放,但高并发下仍可能短暂卡住SQL线程 - 改列名(
CHANGE COLUMN)或改类型(MODIFY COLUMN):即使主库成功,从库大概率退化为COPY,必须提前在从库测试
最危险的是“看起来能跑通”的操作:比如ALTER TABLE t ALTER COLUMN c SET DEFAULT '2024-01-01',MySQL 8.0.23之前主库不锁,但从库SQL线程执行时会获取MDL_WRITE锁,阻塞所有对该表的查询长达数分钟。
FLUSH TABLES WITH READ LOCK前,必须确认INFORMATION_SCHEMA.INNODB_TRX里没有运行超30秒的事务;应用切换后,HikariCP连接池里的旧连接可能还在往旧主写数据,得主动调用evict()或重启服务。


















