Redis-缓存一致性

首先理解什么是缓存一致性,一般一致性分为两大类,强一致性和弱一致性。两者的选择要根据实际业务进行考量舍取。
强一致性要求全部节点在任何时候读取到的数据都是一样的在,这通常需要为此付出更高的代价实现。弱一致性是允许系统在一段时间内读取到不同的数据,通常要求最终的一致性。

缓存一致性包含两层含义:

  1. 缓存中有数据时,数据和数据库的必须一致。
  2. 缓存中没有数据时,数据库的数据必须是最新的值。

把缓存策略分成两种:只读和读写。只读缓存能加速读请求,而读写缓存可以同时加速读写请求。

只读缓存

上图是只读缓存的流程图:

  1. 读请求,先读取 Redis ,命中就返回,没命中就读取数据库,并回写到 Redis。
  2. 写请求,直接写进数据库,不会更新缓存,而是删除 redis 中的数据。
  3. 等下次读请求的时候,就能把数据库的最新数据写到缓存中了。

分析下上述流程,可能存在的一致性问题:

  1. 先删除缓存再更新数据库
    a. 更新数据库失败,会导致数据丢失,新请求读取的是数据库的旧数据。
    b. 请求并发时,在更新数据库前,缓存的数据被删除了,新的读请求会直接读取数据库的旧数据,并把这个旧值写到缓存中。之后的全部读请求都是错误数据。
  2. 先更新数据库再删除缓存
    a. 删除缓存失败,会导致请求读取到缓存的旧数据。
    b. 请求并发时,在删除缓存前,新的读请求会读取到旧数据。等缓存删除就没有影响了。

那么如何解决以上一致性问题?

  • 对于 1.a 和 2.a 这类执行失败的情况,可以进行失败重试。
  • 对于 1.b 采用延迟双删策略,即在更新数据库后,间隔一小会时间再删除一次缓存。
  • 对于 2.b 只会存在短暂的数据不一致。符合最终一致性。

如果业务上要求强一致性,可以在先更新数据库再更新缓存的策略基础上,在更新数据库时,加锁阻塞读请求,等更新完成后才能读取。对于并发更新问题,要保证更新的顺序,也是通过加锁来控制。

综上所述,推荐使用更新数据库,再删除缓存的只读缓存方案。

读写缓存

上图是读写缓存的流程图:

  1. 读请求,先读取 Redis ,命中就返回,没命中就读取数据库,并回写到 Redis。
  2. 写请求,有两种方案,异步回写和同步直写。
  3. 异步回写直接更新缓存,并返回,加快接口响应速度。数据库异步进行更新。
  4. 同步直写是缓存和数据库都写入数据后再返回。

对于读写缓存,视业务情况来使用异步写回还是同步直写,对于读写性能要求都很高的业务采用异步写回,数据都直接在缓存中操作。需要承担部分数据丢失风险,和数据不一致的风险。实际上大多数业务并没有那么高的一致性要求。

对于读多写少的场景,采用同步直写方式。只要给数据库和缓存操作加上事务控制,保证原子性。即可避免缓存一致性问题。

总结

解决缓存一致性要根据实际业务来分析,往往保持最终一致性就行,短时间内的不一致是可以接受的。提供以下方案:

采用先更新数据库再删除缓存的策略,保证数据库数据的正确。失败重试策略,延迟异步双删策略删除缓存。

更有甚者,监听数据库的变化,使用 binlog 日志异步检查数据库和缓存的一致性,不一致时,删除缓存。

SystemCaller
SystemCaller

https://gravatar.com/noisily745e35dad0

文章: 47

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注