最近发现了一个主从相关的问题,在这里记录一下。
一、背景:在业务过程中产生的财务数据需要发送给财务团队。然后公司已经有相关的服务A,通过Binlog Dump实时获取数据库的增量日志,并通过解析后获取具体的数据变更,最后将变更记录推送到Kafka中以供业务方消费。于是我们的方案就是消费A服务产生的变更记录,然后联表查询其他表的字段(因为财务团队需要的数据包通常是多表的,但是A服务产生的变更记录是基于单表),聚合之后投递出去。
出现的问题:现在有一份财务需要的财务信息,包括c表和d表的信息。A服务解析出c表和d表的变更,会产生两份变更记录,然后基于c表和d表的两份变更记录进行联表,然后发现在c表进行联表查询d表的信息 的时候发现没有查到d表的信息,但是c表中已经包含有d表的相关主键id了。因此只能说明是由于主备延迟导致的。
二、分析:看代码和数据库记录发现c表和d表的变更是基于同一个事务产生的,ctime和mtime都是一样的。对于同一个事务来说,那么应该是同时提交到binlog中的?但是binlog产生之后,但是从库查询的时候查不到,那基本说明就是主从延迟了,主从延迟其实最主要的原因是从relay log写入到从库表太慢导致,对于Binlog Dump和从库同步来说,都是从主库进行同步的,但是Binlog Dump通过Kafka先到了我们的服务,但是从库还差不到,那就是从relay log写入到从库表中太慢了。那么怎么看主从之间的延时呢?怎么监控主从之间的延时情况?以及同一数据变更多次事务是怎么写入binlog的呢?Dump binlog的基于文件位置和基于GTID有什么区别呢?
image.png其实,binlog 的写入逻辑比较简单:事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了 binlog cache 的保存问题。
通过公司的MySQL监控工具看了下相应库的主从延时,发现延时比较大的时候能够达到1s,1s的时间足够将消息处理完并发出去了,因此就会带来上述问题。这样带来的问题就是可能会丢信息,因为c表查询d表数据不存在,就不会发消息,然后d表查c表查不到,相应字段会设为空,那么c表的字段就会丢失。现在数据量少,只有一条数据,丢数据的情况不多见,如果数据很多的话风险就比较大。
三、怎么解决?
- 强制走主库方案;
- sleep 方案;
- 判断主备无延迟方案;
- 配合 semi-sync 方案;
- 等主库位点方案;
- 等 GTID 方案
另外可以优化的地方就是尽可能减少主从延时的时间,思路基本就是较少大事务、使用基于事务组提交的事务并行复制、调节两个参数binlog_group_commit_sync_delay、binlog_group_commit_sync_no_delay_count
引用极客时间的文章
binlog_group_commit_sync_delay 参数,表示延迟多少微秒后才调用 fsync;binlog_group_commit_sync_no_delay_count 参数,表示累积多少次以后才调用 fsync。这两个参数是用于故意拉长 binlog 从 write 到 fsync 的时间,以此减少 binlog 的写盘次数。在 MySQL 5.7 的并行复制策略里,它们可以用来制造更多的“同时处于 prepare 阶段的事务”。这样就增加了备库复制的并行度。也就是说,这两个参数,既可以“故意”让主库提交得慢些,又可以让备库执行得快些。在 MySQL 5.7 处理备库延迟的时候,可以考虑调整这两个参数值,来达到提升备库复制并发度的目的。
网友评论