Spring事务和缓存导致的数据不一致问题

た 入场券 2023-10-06 16:53 155阅读 0赞

目录

一、问题现象

二、初步分析

三、问题定位

四、解决方案

五、问题发酵

六、最终方案

七、总结


一、问题现象

核心系统进行交易时,偶现交易日期与日切日期不一致的情况。

二、**初步分析**

1、日切日期统一由日切服务维护,每次交易前,会先调用日切服务获取当前日切日期,因此,问题源头应在日切服务;

2、因为日切服务只做简单的更新和查询操作,而且不一致的情况属于偶现,所以不应该是日切的功能出现异常;

3、日切服务维护了日切日期的缓存,因此,数据不一致的问题可能出在缓存上;

三、**问题定位**

1、通过梳理代码,分析日切缓存的运作机制

(1)调用日切服务进行日切时,会首先查询得到当前日切日期。

2021031717481847.png

(2)然后,查询当前日切信息;操作数据库,在当前日切日期上加1天;将之前获取的日切信息插入操作历史中;查询更新后的日切日期,并返回。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpWmhlbjMxNA_size_16_color_FFFFFF_t_70

(3)在进行日切日期更新前,会先清空缓存(注:CacheEvict的beforeInvocation参数设置为true,表示在方法执行前清空缓存,默认为false)。

20210317175141502.png

2、至此,得到完整的日切操作流程如下:

获取当前日期—>清空缓存—>更新数据库—>查询更新后的结果并返回

单独执行此流程不会存在任何问题,缓存数据与数据库保持一致

但是,清空缓存到数据写入数据库之间存在时间差(当数据库分主从时,主从同步的时间差也应被考虑进来),在此时间差内,如果有查询操作,会将当前未被修改的日切日期写入缓存。

如此一来,当数据被写入数据库后,缓存与数据库的数据即不一致,查询得到的结果都是缓存中的脏数据,真相大白。

四、解决方案

将缓存清空的时间,调整到更新数据库之后进行

20210317175232796.png

五、问题发酵

解决了上一个问题之后,在梳理代码的过程中,发现了另外一个设计问题,即:在事务中使用缓存

在事务中使用缓存是一个典型的设计漏洞,现以本场景为例进行说明

日切时,在事务中进行的数据操作为(@Transactional方法内):

获取当前日切信息—>更新数据库—>清空缓存—>将之前获取的日切信息插入日切历史表—>查询当前最新日切日期(先查缓存,缓存为空,然后查数据库)–>写入缓存

分析可知,清空缓存到插入日切历史表之间存在时间差,在此时间差内,如果有查询操作,则最新的数据在事务完成前被写入缓存中,如果插入日切历史表失败,导致事务回滚,则数据库中日切日期与缓存不一致。

对于事务而言,其中使用缓存会存在的最大风险便是:事务回滚后,缓存与数据库中数据不一致。

六、最终方案

进行日切操作时,去除缓存。

20210317175303302.png

七、**总结**

1、使用缓存时,一定要考虑缓存的更新时机,对于高并发场景,很容易造成数据不一致的问题;

2、切记:事务中不要使用缓存,否则剁手;

发表评论

表情:
评论列表 (有 0 条评论,155人围观)

还没有评论,来说两句吧...

相关阅读