数据同步与事务碰撞

1.事件起因

在项目开发过程中数据同步是经常遇见的事情,同步方法常见有:定时任务扫表同步、通过消息队列[MQ、Kafka等]同步,从数据的角度来划分也有:全量数据同步、增量数据同步、变化数据同步,根据时间角度划分也有:准实时同步,延时同步

在这次我们自己的项目开发中需要A库增量及变化数据实时同步B库,以供B应用能直接在B库中查到,但是我们在代码编写完成,交付给测试组人员测试过程中,测试发现A库变化某条数据后版本号+1,比如版本号为3,计划数目+1,比如结果为9, 但是B库里面的版本号同步过去是2,计划数目为8,当A库再次更新该条记录,版本号累加到4,计划数为10时,B库同步到的数据是版本号为3,计划数为9。 A库于B库是同步数据了,但是同步的数据永远差一个版本。

2.事件分析

1.第一时间,我认为是Kafka机器下标计算错误,需要重启Kafka对应的机器,让其下标正确,后经过努力找到管理Kafka机器工作人员,帮忙重启Kafka对应ip机器后,测试再次进行验证,但依旧会A库于B库之间版本差一。

2.再次进行代码分析,发现更新数据的Service里面,带了一个事务,进行了更新后,再调用同步的Service里面,先查了一遍A库的数据,再同步给B库的,我认为在一个事务里,update的数据是还没有更新到数据库里面去的,这时候再调用同步的Service去查A库的数据,得到的就是旧的数据,然后同步给B库,事务再进行提交,A库更新为最新的数据,这样就能够解释得通。

3.经过同事提醒,第二个推论是有问题的,一个事务内update数据后,再查其实就是最新的数据后,我们再次认真看了编写的代码发现,再调用同步的Service由自己新起了一个事务,导致在这个事务里面查到的数据就是旧的数据,这才是最重的原因,最后将调用同步的Service的@transactional注解给去掉后,测试再进行测试数据就正确了.

3.原理分析

可以看到第二次与第三次的分析,是有一个点的就是事务内update数据,再查是新的数据还是旧的数据的问题,这里就需要理解事务的ACID特性里面的I:隔离性

脏读 不可重复读 幻读
Read Uncommitted 可能 可能 可能
Read Committed 不可能 可能 可能
Repeatable Read 不可能 不可能 可能
Serilization 不可能 不可能 不可能
  1. 脏读:如果一个事务A对数据进行了更改,但是还没有提交,而另一个事务B就可以读到事务A尚未提交的更新结果。这样,当事务A进行回滚时,那么事务B始读到的数据就是一笔脏数据。
1:小明工资1000,财务人员将小明工资改成8000,但是未提交事务。 
2:小明发现自己工资8000(事务未提交期间)。 
3:财务发现操作错误,回滚事务。工资变成1000。 描述:在事务未提交的时候,另一个事务读取到了未提交事务的数据。		
  1. 不可重复读:同一个事务在事务过程中,对同一个数据进行读取操作,读取到的结果不同。例如,事务B在事务A的更新操作前读到的数据,跟在事务A提交此更新操作后读到的数据,可能不同。
    要避免不可重复读,需要将事务所操作的记录都加上锁,不允许其他事务对此记录进行写操作。
1:事务1中,小明发现自己工资1000,操作没有完成。
2:事务2中,财务修改小明的工资为8000,提交事务。
3:事务1中,小明再次读取自己工资发现变成了8000。
描述:针对与修改数据,一次事务读取数据前后两次不一样。(愿意期间被别的事务修改了值)
  1. 幻读:同样一个查询在整个事务中多次执行,查询所得的结果不同。例如,事务A对全部记录做了更新操作,尚未提交前,事务B又插入了一条记录,那么事务A再次读取数据库时,会发现还有一条记录(即事务B新插入的记录)没有做更新。
1:事务1中,读取工资为1000的有1000个员工。
2:事务2向员工表新增一条员工数据,工资也是1000.
3:事务1中,再次读取发现工资为1000的员工有1001个。
描述:针对新增或者删除,一次事务读取数据前后两次不一样。(愿意期间被别的事务新增或者删除了值)

Mysql-InnoDb 默认的隔离级别是Repeatable Read

Oracle 默认的隔离级别是Read Committed

posted @ 2021-10-16 14:29  宇。  阅读(176)  评论(0)    收藏  举报