1、现有MySQL与ES同步架构
image.png
2、线上问题描述
实际运行中发现,在不定的期间内,存在ES中订单的数据与数据库中不一致的情况,分析产生原因的可能性:
1、MQ消息丢失,导致ES没收到消息从而没有更新数据;
2、读取数据库的数据不是最新数据;
3、问题跟踪与解决
针对原因1:在程序中加入日志,发现在出问题的时候,消息是能够收到并且是正确的;
针对原因2:开始怀疑是主从延迟导致,把读取改到主库上,问题依旧(实际上此步骤是多余的,没有经过仔细分析,因为canal读取的是从库的binlog,一旦数据写入binlog说明数据在从库中肯定已经有了);可能出现问题的情况都尝试过了,问题依旧,困扰了好几天,直到看到了一篇文章《从一个线上问题分析binlog与内部XA事务提交过程》,里面分析了MySQL内部XA解决redo log与binlog一致性问题的方式
第一阶段:InnoDB prepare, write/sync redo log;binlog不作任何操作;
第二阶段:包含两步,1> write/sync Binlog; 2> InnoDB commit (commit in memory);
当第二阶段的第1步执行完成之后,binlog已经写入,MySQL会认为事务已经提交并持久化了(在这一步binlog就已经ready并且可以发送给订阅者了)。在这个时刻,就算数据库发生了崩溃,那么重启MySQL之后依然能正确恢复该事务。在这一步之前包含这一步任何操作的失败都会引起事务的rollback。
第二阶段的第2步大部分都是内存操作,比如释放锁,释放mvcc相关的read view等等。MySQL认为这一步不会发生任何错误,一旦发生了错误那就是数据库的崩溃,MySQL自身无法处理。这个阶段没有任何导致事务rollback的逻辑。在程序运行层面,只有这一步完成之后,事务导致变更才能通过API或者客户端查询体现出来。
下面的一张图,说明了MySQL在何时会将binlog发送给订阅者。
image.png
通过瓷片文章的启发,结合系统出现问题的情况,分析极大可能是因为binlog的变化在commit之前,导致程序在读取MySQL的时候数据还没变化,所以出现ES与MySQL数据不一致。
解决方法:使用RocketMQ的延迟消息,造成时间差,读取数据库;修改上线之后,一直运行稳定,未出现数据不一致情况。
注:事后分析造成commit提交延迟,很有可能是因为当时提交的是一个大事务导致,这个怀疑还没有去证明。
网友评论