双写一致性的讨论

简介

双写一致性的目的:我们要达到最终一致性!

给缓存设置过期时间,是保证最终一致性的解决方案。

我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记以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解决方案:

image-20211122104027581

总结

image-20211124102214342

原文地址:https://www.cnblogs.com/wwjj4811/p/15596968.html