简介
双写一致性的目的:我们要达到最终一致性!
给缓存设置过期时间,是保证最终一致性的解决方案。
我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记以mysql的数据库写入库为准。
下面讨论三种更新策略:
- 先更新数据库,再更新缓存
- 先删除缓存,再更新数据库
- 先更新数据库,再删除缓存
先更新数据库,再更新缓存
异常问题:
1.先更新mysql的某商品库存,当前库存是100,更新到99
2.更新mysql成功,然后更新reids
3.此时假设异常出现,更新redis失败,这会导致mysql里面库存是99,redis里面还是100
4.上述发生,会让数据库里面和缓存redis里面数据不一致,读到脏数据。
先删除缓存,再更新数据库
异常问题:
- 先删除redis里面的数据,然后正在更新mysql时,但是还没有更新结束,另一个线程突然出现要来读取缓存数据,此时redis里面数据是空的,从mysql获得了旧值,并把旧值写回redis(刚被删除的数据有可能被写回到redis)
- 高并发情况下,可能造成缓存击穿或缓存穿透
延时双删方案
在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。具体步骤是:
1)先删除缓存
2)再写数据库
3)休眠500毫秒(根据具体的业务时间来定)
4)再次删除缓存。
那么,这个500毫秒怎么确定的,具体该休眠多久呢?
需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
当然,这种策略还要考虑 redis 和数据库主从同步的耗时。最后的写数据的休眠时间:则在读数据业务逻辑的耗时的基础上,加上几百ms即可。比如:休眠1秒。
当然第二次删除可以采用异步删除。
先更新数据库,再删除缓存
出现问题如下:
1.线程A先修改数据库中的值,还没有来得及删除缓存,然后线程B读取到缓存的旧数据,最后线程A更新缓存的数据。
canal解决方案: