一、问题现象

背景: 生产环境出现主从同步的故障, 出现的错误为HA_ERR_KEY_NOT_FOUND, 错误码为1032,检查主从数据没有出现缺失, 表结构和数据都完全一致, 经过排查问题是在于主从同步的配置,这里以示例做模拟重现,逐步分析定位出具体原因。

1、升级后, 主从同步出现故障, 错误码为1032,进入从节点, 查看主从同步状态

show slave status\G

出现如下错误:

mysql主从 io是no mysql主从1032_sql

2、查看具体的错误信息

select * from performance_schema.replication_applier_status_by_worker;

可以看到错误和事务序号信息:

mysql主从 io是no mysql主从1032_mysql_02

这里提示错误的binlog文件为binlog.00025, 下面需要分析此日志文件。

二、原因分析

1、首先对binlog日志进行分析

  • 解析主节点的binlog日志

/usr/local/mysql/bin/mysqlbinlog --no-defaults -v /datadir_3306/log/binlog.000025 --base64-output=decode-rows > bin.000025.txt

  • 根据事物信息,搜索解析后的bin.000025.txt文件:

    @1对应的是表的第一个字段, @2是第二个字段,根据条件信息, 去从库里面查找, 发现是可以找到对应的记录。
  • 查看表结构信息
  • mysql主从 io是no mysql主从1032_数据库_03

  • 生产的这次升级, 是去除了原有的ID自增主键, 保留了唯一索引, 未升级之前是可以正常同步, 于是要在本地验证下, 这种表结构是否可以正常同步。

2、本地主从同步验证

本地搭建的主从同步环境, 采用默认的主从同步配置,验证发现, 无论是新增删除、还是对表结构复原,删除主键ID,再插入数据, 都是可以正常同步。

这里有个特殊之处, 是在于应用程序的写入, 是采用insert into … on duplicate key update 操作,也就是不存在新增数据, 存在则根据唯一索引更新数据, 但经过实际多次的写入验证, 无论是新增还是更新, 都能够正常进行同步。

3、最后检查生产环境的主从同步配置

问题原因可能出现在主从同步配置上, 为了能够重现, 找出真正原因, 拿到生产的配置, 重新再做主从同步验证。 生产的主从同步配置:

sync_binlog=1 
gtid_mode=on
enforce_gtid_consistency=1
log_bin=/mydata/3306/log/bin.log
log_bin_index=/mydata/3306/log/bin.index
log_slave_updates=1
binlog_format=ROW
binlog_rows_query_log_events=1
relay_log=/mydata/3306/log/relay-bin.log
relay_log_index=/mydata/3306/log/relay-bin.index
relay_log_info_file=/mydata/3306/log/relay-log.info
relay_log_recovery=1
slave_skip_errors=ddl_exist_errors
slave-rows-search-algorithms='INDEX_SCAN,HASH_SCAN'
innodb_flush_log_at_trx_commit=1
max_binlog_size=100M
skip-slave-start=1
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=8
slave_preserve_commit_order=1
slave_transaction_retries=128
# 采用半同步模式
plugin_dir='/usr/local/mysql/lib/plugin'
plugin_load="validate_password.so;rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=3000
rpl_semi_sync_slave_enabled=1

这份配置生产一直运行并未出现问题, 初步检查,也没发现什么问题,按此配置更新

1)停止主从同步

stop slave;

2)重置主从同步

reset slave;

3)查看并记录主节点的同步偏移量

show master status\G

mysql主从 io是no mysql主从1032_数据库_04

日志文件为binlog.000024, 偏移量为1567321。

4)修改从节点的同步信息

change master to master_host=‘10.16.130.79’,master_user=‘replica’, master_password=‘replica’, master_port=3306, master_log_file=‘mysql-bin.000024’, master_log_pos=偏移量为1567321, master_connect_retry=30;

5)开启主从同步

start slave;

4、再次进行数据同步, 问题终于重现

将表结构的ID重新去除, 只保留唯一索引, 进行新增数据的操作, 出现了上面的主从同步的问题, 到底是什么问题? 检查发现此项配置可疑: slave-rows-search-algorithms=‘INDEX_SCAN,HASH_SCAN’, 将此项配置注掉后, 重新进行主从同步验证, 问题消失。

三、问题总结

1、slave_rows_search_algorithms参数的作用

此参数是从节点接收到数据时, 采用何种方式进行数据查找, 取值如下:

  • TABLE_SCAN,INDEX_SCAN(默认值)
  • INDEX_SCAN,HASH_SCAN
  • TABLE_SCAN,HASH_SCAN
  • TABLE_SCAN,INDEX_SCAN,HASH_SCAN

上面出现问题的原因, 是在于没有启用TABLE_SCAN全表扫描,导致主从同步更新时, 报出HA_ERR_KEY_NOT_FOUND没有找到数据的错误, 但实际数据是存在,虽然有联合唯一索引, 但写入SQL的操作方式并不生效:

mysql主从 io是no mysql主从1032_主从同步_05

导致走HASH_SCAN方式。

2、HASH_SCAN方式存在缺陷

查阅相关资料, 发现主从同步走HASH_SCAN方式, 也出现过类似缺陷:

mysql主从 io是no mysql主从1032_mysql_06

3、建议的解决方式

如何保障性能效率, 又能很好的解决此问题:

1) 不用修改主从同步参数slave-rows-search-algorithms

仍采用“INDEX_SCAN,HASH_SCAN“方式, 这样是可以提升主从同步的效率

2)表结构必须加上主键(可以是自增ID也可以是联合主键), 从检索速度和稳定性来讲, 没特殊需要, 不建议采用字符类型作为主键。