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

目录

一、问题现象

二、初步分析

三、问题定位

四、解决方案

五、问题发酵

六、最终方案

七、总结


一、问题现象

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

二、初步分析

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

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

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

三、问题定位

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

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

            

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

                 

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

           

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

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

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

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

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

四、解决方案

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

     

五、问题发酵

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

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

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

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

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

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

六、最终方案

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

    

七、总结

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值