Redis缓存一致问题(延迟双删)

今天遇到一个线上问题,很有意思,也很难得。之前就有学习到一致性的解决问题,今天终于来了一个上线案例了,好在并发场景并不得多。~~

场景:

拼车子单再确认接单的过程中,会生成对应司机的形成单号,并最终落库。落库的时候,用到了Redis实现的延迟双删,保证缓存与数据库保持一致;另一个线程操作表中相同的记录行,记录该订单免抽佣。两个线程共用一把分布式锁。按道理看,一个线程执行完,保存数据,释放锁,另一个线程才会操作相同数据行,没什么问题。问题是还有一个线程3,简单画个图。

线程时间轴(格子不能表示时间长短,只能表示时间顺序) A B C D E F G H I J K L M 线程1 查缓存(无) 查库(旧) 存缓存(旧) 返回数据 线程2 获取分布式锁 查库(旧) 逻辑处理 提交事务(新1) 删除缓存 释放分布式锁 延迟删除缓存(200ms) 线程3 获取分布式锁 查缓存(旧) 业务处理 提交事务(新2) 删除缓存 释放分布式锁 延迟删除缓存(200ms)
线程表格图

上述三个线程并发,主要问题是线程2更细数据库,并删除缓存后,线程1把旧的数据保存到缓存。导致线程3用旧的数据处理业务逻辑并保存到了数据库。

简单分析:

线程2和线程3共享分布式锁,所以相互隔离,没有问题。问题出现在线程1的F列持有旧的缓存,更新进了Redis。在线程2延迟删除缓存没操作时,被线程3用到了缓存数据。多个线程并发操作一行数据的修改和缓存的存储就会有这个问题。

解决:

1.线程3查缓存(旧)改成查数据库。虽然并发问题是不可避免的,但是数据库事务为我们做了这些,考虑到每条订单记录修改量不大可以直接查库。

2.考虑到项目处理订单数据都是状态机processor中处理,也就是修改数据过程中都是持有分布式锁的。那么可以考虑释放分布式锁之前,手动把最新的数据放入缓存。

总结

遇到问题还是要具体分析的,每一种解决方案都不是完美的,但每种解决方案都有最适合他的场景。上面我们选用的方案1。其中还可以考虑延迟删除缓存的时间,我们业务设置的200ms,这种想法只能缩小并发问题,不能根本上解决。

小伙伴可以多多评论,给我打开更多的思路,相互学习。

经验分享 程序员 微信小程序 职场和发展