无主键导致从库sql线程“卡死”,因row模式下需用前镜像逐行定位,无主键或唯一索引时只能全表扫描;删10万行即10万次扫描,耗时数小时,本质是复制机制固有缺陷而非性能问题。

为什么无主键会让从库SQL线程“卡死”
MySQL在binlog_format=ROW模式下,DELETE或UPDATE操作会把每一行变更的完整前镜像(before image)写入binlog。从库回放时,必须根据这些字段值,在本地表中精准定位到对应行——没主键、也没唯一索引,InnoDB就无法用索引快速查找,只能逐行比对整张表。
删10万行?就是10万次全表扫描。表本身有500万行,单次扫描耗时0.2秒,总耗时≈5.5小时。你看到SHOW PROCESSLIST里SQL线程长期卡在Deleting或Updating,Seconds_Behind_Master持续飙升,不是慢,是逻辑上根本找不到高效路径。
这不是配置调优能解决的性能问题,而是复制机制在无主键场景下的固有缺陷。
紧急止损:跳过阻塞事务但不掩盖数据不一致
当从库已卡住且业务不可停,目标是先恢复复制流流动,再处理一致性:
- 先执行
STOP SLAVE;,避免relay log继续堆积 - 确认是GTID模式:用
SET GTID_NEXT='xxx:yyy'; BEGIN; COMMIT;注入空事务;是position模式:用SET GLOBAL sql_slave_skip_counter = 1;后START SLAVE; -
切勿重启从库后再跳过——
sql_slave_skip_counter是会话级变量,重启即丢失 - 跳过只是让SQL线程往前走,被跳过的DML操作在从库仍存在,后续必须人工补删,否则主从数据不一致
根治方案:加主键 + 调参 + 改操作习惯
修复不是修一条SQL,而是改三处:
-
加显式主键:在主库执行
ALTER TABLE db_name.tbl_name ADD COLUMN id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY FIRST;;从库同样执行(需先STOP SLAVE) -
调
slave_rows_search_algorithms:设为'TABLE_SCAN,INDEX_SCAN,HASH_SCAN'可显著缓解无主键表的延迟(测试显示4万行更新延迟从576秒降至64秒),但它只是临时缓冲,不能替代主键 -
禁用全表DELETE/UPDATE:上线前强制校验DDL,拦截无主键建表;清理类操作改用
TRUNCATE TABLE(记为DDL事件,不走row binlog);大表删改必须带WHERE条件+索引,且分批执行,如DELETE FROM tbl WHERE id BETWEEN ? AND ? LIMIT 10000
容易被忽略的细节
很多人以为加了普通索引就能解决问题,但实际不行——只要没主键或唯一索引,slave_rows_search_algorithms里的INDEX_SCAN依然无效,因为InnoDB无法保证索引值唯一,仍需逐行比对binlog中的完整行镜像。
另外,ROW_ID是InnoDB隐式生成的聚簇索引键,但它在主从实例间完全独立、不互通。并行复制开启后,事务顺序错乱会导致从库HA_ERR_KEY_NOT_FOUND(错误码1032),这不是数据丢了,是定位机制彻底失效。











