背景:
系统里有一个修改数据的方法,在修改数据之后,删除了redis的缓存
写缓存是通过下次查询时懒加载写入到redis的
问题:
测试的时候发现这个缓存删不掉
分析:
怀疑是不是没有执行,或者执行失败了
经过调试发现删除缓存的代码确实是执行了,且执行成功
怀疑是不是在删除缓存之后,有人刚好调用了查询数据,这也太巧了吧
确认了一下,方法上有事务。也就是说在删除缓存之后,事务还没有提交,数据库中的数据还不是新数据,这时候有人调用了查询接口。这个推论很合理,但是就是太巧合,不可能这么巧。
又调用了几次,发现缓存的数据每次都是最新的上个版本,与推论吻合。
删除缓存之后的代码执行需要很长时间吗?往下看代码发现推送消息给了另外一个系统。再看查询数据的接口,对外提供了rpc接口,然后发现那个系统确实在那个时间点调用了这个rpc接口。
这就讲得通了:
A系统在删除缓存之后,推了消息给B系统,后面还有一段代码导致方法没有立即结束,B系统收到消息之后立马来请求了数据,响应实在是太快了,由于A系统事务还没有提交,导致B系统请求到的是旧数据,并写入了缓存。完美,合情合理。
解决:
- 方案一,删除缓存之后立马将新数据写入到缓存,存在隐患,如果后续代码发生异常导致数据回滚,则缓存与数据库不一致
- 方案二,去掉事务,适合可以接受去掉事务的场景,前提是需要谨慎评估去掉事务后的影响
- 方案三,删除缓存之后立马结束事务,后续逻辑在事务之外执行,具体业务具体分析