所谓缓存,实际上是借助高速存储介质来提高读功能的一种技能。从工程角度来看,缓存能够分为本地缓存分布式缓存,本地缓存一般由静态变量来承载,其能够完结类间同享与进程内同享,但无法做到进程间同享,而分布式缓则能够完结进程间同享的,分布式缓存常常由 Redis 来承载。

引进分布式缓存不只能够明显提高查询功能与吞吐量,还能够为数据库屏蔽大流量的冲击,但技能往往是一把双刃剑,在享用分布式缓存红利的一起,它也带来了数据共同性挑战。在分布式环境下,数据库与分布式缓存之间一定会存在数据不共同的景象,咱们要做的便是尽或许缩小数据不共同的时刻窗口,寻求终究共同性!

1 共同性挑战

引进分布式缓存后,数据查询场景往往触及缓存回填,而数据更新场景则触及缓存更新。回填也好,更新也罢,均有或许触发数据不共同问题,但要往根儿上捋的话,缓存更新才是形成数据不共同的元凶巨恶,假如说往 Redis 回填的是旧数据,那必然是在这期间发生了缓存更新。

缓存更新一般有四种组合动作,分别是:

  • 先更新 MySQL 再更新 Redis
  • 先更新 Redis 再更新 MySQL
  • 先更新 MySQL 再删去 Redis
  • 先删去 Redis 再更新 Redis

下面一起来分析下这四种缓存更新动作究竟是怎么形成数据不共同现象的;一起也要探索出哪一种缓存更新动作最佳,数据不共同的时刻窗口最小。在笔者《Redis 分布式锁的再研究》一文中曾说到 Martin Kleppmann 大佬认为 RedLock 是一种不三不四、彻底建立在三种假定 (进程暂停、网络时延、时钟漂移) 基础上的分布式锁计划,确实,进程暂停、网络时延和时钟漂移算得上是 JVM 分布式体系中的三大拦路虎了;为了更直观、清晰地描述数据不共同现象,咱们依然选用进程暂停作为分析手段,其实网络时延也具备相同的效果

先更新 MySQL 再更新 Redis

在 先更新 MySQL 再更新 Redis 动作下,也就不触及缓存回填了。

分布式缓存一致性的再研究

先更新 Redis 再更新 MySQL

在 先更新 Redis 再更新 MySQL 动作下,相同也不触及缓存回填。

分布式缓存一致性的再研究

先更新 MySQL 再删去 Redis

在 先更新 MySQL 再删去 Redis 动作下,触及缓存回填。

分布式缓存一致性的再研究

从上图来看,确实呈现了数据不共同现象,但能确保到达终究共同性,这应该不算大问题,而下面这种景象可就不好了。

分布式缓存一致性的再研究

先删去 Redis 再更新 MySQL

在 先删去 Redis 再更新 MySQL 动作下,相同也触及缓存回填。

分布式缓存一致性的再研究

前两种缓存计划均是一种缓存更新动作,有点费力不讨好,假如缓存更新后一直没有被射中呢,大家应该避免运用。而在后两种计划中,先更新 MySQL 再删去 Redis 是要优于 先删去 Redis 再更新 MySQL 计划的,由于 数据库查询与缓存回填之间的时刻窗口 往往小于 缓存删去与数据库更新之间的时刻窗口,也便是说 先更新 MySQL 再删去 Redis 计划所引发数据不共同现象的概率更低。

2 更好的计划

假如事务场景并发度不高,那选用 先更新 MySQL 再删去 Redis 计划彻底够了,但假如要寻求更高的共同性体验,笔者觉得携程技能大众号上共享的 干货 | 分布式缓存与DB秒级共同规划实践 计划很值得咱们借鉴。该计划主要中心点如下:

  • 在事务层中,只有缓存查询逻辑,不触及任何针对分布式缓存的更新、删去和回填逻辑,针对分布式缓存的更新、删去和回填逻辑由独立的缓存更新渠道担任。
  • 在事务层中,假如数据库中数据发生了改变,则事务层担任将针对 INSERT、UPDATE 和 DELETE 语句的数据改变事情推送到 MQ 中去;为了保证分布式环境下针对同一 key 操作的次序性,需要通过mod(hash(key))这一方法将同一 key 发送到 MQ 中同一行列。
  • MQ 中的数据改变事情由缓存更新渠道担任串行消费,但依然无法百分之百避免数据不共同的问题,或许由于网络颤动等要素导致发送到同一行列中的数据改变事情的次序自身便是错误的,所以要针对缓存数据规划一个版本号,这个版本号能够是更新时刻 (单调递加),那么接下来在真实操作缓存的时候需要比对这个版本号,避免将旧的数据填充到分布式缓存中去。
  • 关于热点 key,缓存更新渠道在完结相应数据操作后,需要向 MQ 发送广播音讯以告诉事务方进行本地缓存数据的更新。

关于 CDC 计划本文就不介绍了,由于 CDC 计划中除了 CDC 东西之外的细节在携程计划中均有所体现。

总结

当然,分布式缓存领域中并不只有数据共同性这一个挑战,还有缓存穿透、缓存击穿和缓存雪崩。

分布式缓存一致性的再研究