别盲目开 hash_scan,尤其当表有 unique key 但无 primary key 时,slave_rows_search_algorithms = 'index_scan,hash_scan' 很可能触发 can't find record 报错,导致复制中断。

直接给结论:别盲目开 HASH_SCAN,尤其当表有 UNIQUE KEY 但无 PRIMARY KEY 时,slave_rows_search_algorithms = 'INDEX_SCAN,HASH_SCAN' 很可能触发 Can't find record 报错,导致复制中断。
为什么改这个参数容易出错
MySQL 在 ROW 模式下回放 DELETE 或 UPDATE 事件时,需要根据前镜像(before image)定位目标行。默认用 INDEX_SCAN,TABLE_SCAN:有索引就走索引,没索引才全表扫。而 HASH_SCAN 的设计前提是「同一 event 中所有变更行互不冲突」——但它不保证顺序、不处理重复键冲突,更不会按语句粒度隔离。
常见踩坑场景包括:
-
REPLACE INTO t VALUES (1,3),(1,4):单个 event 包含两条逻辑上覆盖同一UNIQUE KEY的记录,HASH_SCAN缓存时后写覆盖前写,导致其中一条找不到原记录 - 表只有
UNIQUE KEY,没有PRIMARY KEY:MySQL 内部对唯一键的 hash 构建和查找逻辑存在边界 case,官方文档明确警告TABLE_SCAN,HASH_SCAN组合「会导致 record not found 或 duplicate key 错误」 - 从库执行过 DDL(如列顺序调整),但 binlog 里只按字段序号(@1,@2,…)传递值:
HASH_SCAN不参与列映射,只管找行,容易匹配到错误物理位置
怎么安全地配置 slave\_rows\_search\_algorithms
优先保留默认值,仅在确认必要时微调。生效方式是动态设置,无需重启:
SET GLOBAL slave_rows_search_algorithms = 'INDEX_SCAN,TABLE_SCAN';
如果真要启用 hash 加速(比如大表无任何索引),必须满足:
- 表必须有
PRIMARY KEY,不能只靠UNIQUE KEY - 避免单 event 多行写入冲突操作,例如把
REPLACE INTO ... VALUES (a,b),(c,d)拆成多条单行语句 - 确认 binlog_format=ROW 且主从表结构完全一致(包括列顺序、字符集、生成列等)
- 测试阶段先在从库执行
STOP SLAVE; SET GLOBAL ...; START SLAVE;,观察Seconds_Behind_Master和错误日志
MySQL 8.3+ 用户注意
从 MySQL 8.3.0 开始,slave_rows_search_algorithms 已被移除,内部固定为 HASH_SCAN,INDEX_SCAN,不可配置。这意味着:
- 你不能再手动关掉
HASH_SCAN来规避问题 - 升级前必须确保所有表都有
PRIMARY KEY,否则原来能跑的复制链路可能在 8.3 升级后直接报HA_ERR_KEY_NOT_FOUND(错误码 1032) - 如果无法加主键,只能考虑降级 binlog_format 到 STATEMENT(不推荐,有函数/非确定性风险),或在应用层规避批量冲突写入
最常被忽略的一点:这个参数只影响 ROW 模式下的 Update_rows_log_event 和 Delete_rows_log_event,对 Write_rows_log_event(即 INSERT)完全无效。所以优化重点永远在「删/改」路径,而不是盲目打开 hash 就以为万事大吉。











