只读从库仍会触发行锁,因read_only=ON不阻止拥有SUPER权限的SQL线程执行写操作及加锁;SELECT ... FOR UPDATE等语句在从库同样加锁,可能与复制事务冲突导致等待。

只读从库为什么还会触发行锁?
因为 readonly=ON 只禁用普通用户的写操作,不阻止复制线程自身的写入;而复制线程(sql_thread)拥有 SUPER 权限,它执行的 UPDATE/DELETE 语句照常加行锁。如果此时有业务查询(比如带 SELECT ... FOR UPDATE 或触发唯一索引冲突的 INSERT)恰好落在同一行上,就会和正在回放的复制事务产生锁等待。
哪些只读查询会意外申请写锁?
表面是“只读”,但底层仍需加锁的语句容易被忽略:
-
SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE:显式请求行级 X 锁或 S 锁,即使在从库也会生效 - 带唯一索引冲突检测的
INSERT ... ON DUPLICATE KEY UPDATE:为判断是否冲突,InnoDB 必须对匹配的唯一索引记录加 S 锁(甚至 X 锁),可能和复制中的更新冲突 - 某些
REPLACE INTO或含INSERT IGNORE的语句:同样需要先查后锁,触发间隙锁或临键锁 - 未走索引的
SELECT若配合FOR UPDATE:可能升级为表级锁(尤其在autocommit=0下手动开启事务时)
复制延迟 + 索引缺失 = 高概率锁等待
当从库存在复制延迟,且业务查询依赖的字段缺少有效索引时,问题会急剧放大:
- 复制线程正在回放一条基于
WHERE status=1的大范围UPDATE,因无索引而扫描全表 → 持有大量记录的 X 锁 - 同时,一个运营后台的
SELECT ... FOR UPDATE FROM orders WHERE create_time > '2026-10-12'因没建create_time索引,也全表扫描并尝试加锁 → 和复制线程在中间某行相遇,进入等待 - 这种组合下,
INNODB_LOCK_WAITS里看到的等待方是业务会话,持有方却是sql_thread(其trx_id在information_schema.INNODB_TRX中显示为非用户会话)
怎么确认等待是否来自复制线程?
关键看 SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 部分:
- 找到状态为
waiting for lock的事务,记下它的trx_id - 再找持有锁的事务,检查其
trx_mysql_thread_id是否为 0(表示内部线程)或对应SHOW PROCESSLIST中的system user、sql_thread - 对比
trx_query字段:复制线程的语句通常是原始 binlog event 的重放形式,如UPDATE `db`.`t` SET x=1 WHERE id=123,而非应用层 SQL - 若发现等待链终点是这类内部事务,就说明不是应用逻辑问题,而是复制与只读查询的资源竞争
真正难处理的点在于:你无法直接 kill 掉 sql_thread 来释放锁(会导致主从不一致),只能优化复制效率或规避冲突查询。最隐蔽的坑是——你以为只读就绝对安全,其实只要复制没追平,从库就始终处于“半写入”状态。


















