redis缓存与数据库一致性
Cache Aside Pattern
(1)读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应
(2)更新的时候,先删除缓存,然后再更新数据库
先删除后更新
避免更新过程中新来的读请求读到旧缓存数据。或者修改了数据库后删除缓存操作失败了,就会导致redis与数据库不一致的问题。
为什么不是更新而是删除?
- 避免不必要的计算
- lazy懒加载思想,很多资源查询率并没有那么高,等到用时再去查就可以。
超高并发场景
如果亿级流量超高并发访问,cache aside pattern会出现删除缓存成功,但是更新数据库还没完成时新的读请求就来了,此时读库拿到应该要更新的旧数据又缓存起来,造成数据不一致的问题。
可以设计一个队列让读写操作串行。
- 更新操作先删除缓存,然后放入队列等执行成功后出队
- 访问请求来到时发现缓存为空并且队尾有排队的更新操作,那么他就需要排队,等更新操作完成后再访问,完成后出队
- 访问请求来到时发现缓存为空并且队尾有排队的访问请求,不排队但是需要等待队列为空后再访问缓存
该解决方案要注意读请求长时阻塞
由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回
该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时
务必通过一些模拟真实的测试,看看更新数据的频繁是怎样的
另外一点,因为一个队列中,可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试,可能需要部署多个服务,每个服务分摊一些数据的更新操作
如果一个内存队列里居然会挤压100个商品的库存修改操作,每隔库存修改操作要耗费10ms区完成,那么最后一个商品的读请求,可能等待10 * 100 = 1000ms = 1s后,才能得到数据
这个时候就导致读请求的长时阻塞
一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会hang多少时间,如果读请求在200ms返回,如果你计算过后,哪怕是最繁忙的时候,积压10个更新操作,最多等待200ms,那还可以的
如果一个内存队列可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少