一般来说,引入缓存就意味着需要接受数据库和缓存存在不一致的情况,如果确实要严格保证双写一致性,有方案就是将请求串行化(不考虑数据库主从)。缺点就是性能较低,吞吐量下降。
一、读写锁
JVM读写锁:
写请求
1、加写锁
2、删除缓存
3、更新数据库
4、回写缓存
5、释放写锁
读请求
1、尝试读取缓存,有则直接返回
2、加读锁
3、获取缓存值(因读写互斥,故可以保证缓存值不可能为空)
4、释放读锁
分布式读写锁:
ZK、REDIS
问题:对于读写锁当读的操作远远大于写操作的时候会增加程序很高的并发量和吞吐量。虽说在高并发的情况下,读写锁的效率很高,但是当读并发很高时读操作长时间占有锁,导致写锁长时间无法被获取而导致的线程饥饿问题,因此在JDK1.8中又在ReentrantReadWriteLock的基础上新增了一个读写并发锁StampedLock。
二、内存队列
高并发场景下的redis缓存和数据库双写不一致问题分析与解决方案设计
简单来说就是将对同一个KEY的读写请求路由到同一个队列中串行化执行
问题:
读请求长时阻塞
读请求并发量过高
路由
热点缓存
三、延迟双删——休眠异步淘汰
主从DB与cache一致性
1、删除缓存
2、更新数据库
3、另起线程异步休眠2S再次删除缓存
总结
感觉这个问题有点扯淡,就像开头说引入缓存就意味着接受缓存延迟,以上方案只是一些假设方案,如果要求强一致性的需求可以直接读主库。
网友评论