一、缓存双写一致性
- 如果redis中有数据:需要和数据库中的值相同。
- 如果redis中没有数据:数据库中的值要是最新值,且准备回写redis。
- 只读缓存。
- 读写缓存:①、同步直写策略:写数据库后也同步写redis缓存,缓存和数据库中的数据一致,对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略。②、异步缓写策略:正常业务运行中,mysql数据变动了,但是可以在业务上容许出现一定时间后才作用于redis,如仓库、物流等功能。异常情况出现了,不得不将失败的动作重新修补,有可能需要借助kafka或者rabbitMQ等消息中间件,实现重试重写。
- 双检加锁策略:多个线程同时去查询数据库的这条数据,那么就可以第一个查询数据的请求上使用一个互斥锁来锁住它。其他线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
1.1、数据库和缓存一致性的更新策略
目的:给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。
可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。就是如果数据库写入成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,要以mysql的数据库写入库为准。
1.1.1、在停机的情况下
给出公告,服务升级,单线程,这样重量级的数据操作最好不要多线程。
1.1.2、先更新数据库,再更新缓存
1、情况1:①、先更新mysql的某商品的库存,当前商品的库存是100,更新为99。②、先更新mysql修改为99成功,然后更新redis。③、出现异常,更新redis失败了,导致MySQL里面的库存是99而redis里面还是100。所以会导致数据库里的数据和缓存redis里面数据不一致,读到redis脏数据。
2、情况2:在多线程环境下,A,B两个线程有快有慢。①、A更新mysql为100。②、B更新mysql为90。③、B先更新redis为90。④、A再更新redis为100。所以导致redis与mysql更新的数据不一致。
1.1.3、先更新缓存,再更新数据库
不推荐:业务上一般把mysql作为底单数据库,保证最后解释。
1.1.4、先删除缓存,再更新数据库
1、请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql……A还没有彻底更新完mysql,还没commit。
2、请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了)。
3、请求B继续,去数据库查询得到了mysql中的旧值(A还没有更新完)。
4、请求B将旧值写回redis缓存。
5、请求A将新值写入mysql数据库。
这样依然会导致数据不一致的情况发生。
解决方法:采用延时双删策略,A线程删除redis缓存,然后sleep一段时间,这期间就是为了让B线程先从数据库读取数据,再把缺失的数据写入缓存,然后线程A再进行删除。所以,线程A sleep的时间,