1、遇到的问题:
护士提交了一封院内压疮文书,应该提交给护士长的,但是护士长却没有看到。每一封文书通过绑定不同的流程来进行不同的处理。
2、问题排查:
初步排查时,登陆人权限设置,不良事件表数据,以及流程表数据,文书绑定的流程都没有问题。继续排查发现,此文书没有到达护士长节点,进一步排查发现走的流程是错误的,但是文书绑定流程功能显示的绑定是正确的,查询此块代码,发现是,文书绑定流程,删除流程时,数据库中数据进行操作删除了,但是redis中的数据没有及时的更新。
3、解决方案:
如果使用redis,那么要保证和数据库数据的一致性。
如果数据在Redis存在,应用就可以直接从Redis拿到数据,不用访问数据库。如果Redis里面没有,先到数据库查询,然后写入到Redis,再返回给应用。
redis做缓存原理一旦被缓存的数据发生变化的时候,我们既要操作数据库的数据,也要操作Redis的数据。
首先需要明确的是,不管选择哪一种方案, 我们肯定是希望两个操作要么都成功,要么都一个都不成功。不然就会发生Redis跟数据库的数据不一致的问题。但是, @Transactional(声明式事务)只能保证数据库的数据一致性问题,但是是无法控制redis中的事务的。
(redis中也是存在事务的。可以使用自定义方法使用编程式事务 我们使用 begin(即控制reids事务也控制数据库事务)、commit、rollback 都需要实现控制redis事务和数据库事务。)
①先操作Redis的数据再操作数据库的数据
②先操作数据库的数据再操作Redis的数据
这里我们先要补充一点,当存储的数据发生变化,Redis的数据也要更新的时候,我们有两种方案,一种就是直接更新,调用set;还有一种是直接删除缓存,让应用在下次查询的时候重新写入。
更新缓存之前,判断是不是要经过其他表的查询、接口调用、计算才能得到最新的数据,而不是直接从数据库拿到的值,如果是的话,建议直接删除缓存,这种方案更加简单,一般情况下也推荐删除缓存方案。
①先更新数据库,再删除缓存
正常情况:更新数据库,成功。删除缓存,成功。
异常情况:
1、更新数据库失败,程序捕获异常,不会走到下一步,所以数据不会出现不一致。
2、更新数据库成功,删除缓存失败。数据库是新数据,缓存是旧数据,发生了不一致的情况。
这种问题怎么解决呢?我们可以提供一个重试的机制。
比如:如果删除缓存失败,我们捕获这个异常,把需要删除的key发送到消息队列。然后自己创建一个消费者消费,尝试再次删除这个key,如图3-5所示。
先删除缓存,再更新数据库
正常情况:删除缓存,成功。更新数据库,成功。
异常情况:
-
删除缓存,程序捕获异常,不会走到下一步,所以数据不会出现不一致。
-
删除缓存成功,更新数据库失败。 因为以数据库的数据为准,所以不存在数据不一致的情况。
看起来好像没问题,但是如果有程序并发操作的情况下: -
线程A需要更新数据,首先删除了Redis缓存
-
线程B查询数据,发现缓存不存在,到数据库查询旧值,写入Redis,返回
-
线程A更新了数据库
这个时候,Redis是旧的值,数据库是新的值,发生了数据不一致的情况,如图3-7所示,这种情况就比较难处理了,只有针对同一条数据进行串行化访问,才能解决这个问题,但是这种实现起来对性能影响较大,因此一般情况下不会采用这种做法。
image-20210705204932980
网友评论