Redis-缓存一致性
目录
显示
首先理解什么是缓存一致性,一般一致性分为两大类,强一致性和弱一致性。两者的选择要根据实际业务进行考量舍取。
强一致性要求全部节点在任何时候读取到的数据都是一样的在,这通常需要为此付出更高的代价实现。弱一致性是允许系统在一段时间内读取到不同的数据,通常要求最终的一致性。
缓存一致性包含两层含义:
- 缓存中有数据时,数据和数据库的必须一致。
- 缓存中没有数据时,数据库的数据必须是最新的值。
把缓存策略分成两种:只读和读写。只读缓存能加速读请求,而读写缓存可以同时加速读写请求。
只读缓存
上图是只读缓存的流程图:
- 读请求,先读取 Redis ,命中就返回,没命中就读取数据库,并回写到 Redis。
- 写请求,直接写进数据库,不会更新缓存,而是删除 redis 中的数据。
- 等下次读请求的时候,就能把数据库的最新数据写到缓存中了。
分析下上述流程,可能存在的一致性问题:
- 先删除缓存再更新数据库
a. 更新数据库失败,会导致数据丢失,新请求读取的是数据库的旧数据。
b. 请求并发时,在更新数据库前,缓存的数据被删除了,新的读请求会直接读取数据库的旧数据,并把这个旧值写到缓存中。之后的全部读请求都是错误数据。 - 先更新数据库再删除缓存
a. 删除缓存失败,会导致请求读取到缓存的旧数据。
b. 请求并发时,在删除缓存前,新的读请求会读取到旧数据。等缓存删除就没有影响了。
那么如何解决以上一致性问题?
- 对于 1.a 和 2.a 这类执行失败的情况,可以进行失败重试。
- 对于 1.b 采用延迟双删策略,即在更新数据库后,间隔一小会时间再删除一次缓存。
- 对于 2.b 只会存在短暂的数据不一致。符合最终一致性。
如果业务上要求强一致性,可以在先更新数据库再更新缓存的策略基础上,在更新数据库时,加锁阻塞读请求,等更新完成后才能读取。对于并发更新问题,要保证更新的顺序,也是通过加锁来控制。
综上所述,推荐使用更新数据库,再删除缓存的只读缓存方案。
读写缓存
上图是读写缓存的流程图:
- 读请求,先读取 Redis ,命中就返回,没命中就读取数据库,并回写到 Redis。
- 写请求,有两种方案,异步回写和同步直写。
- 异步回写直接更新缓存,并返回,加快接口响应速度。数据库异步进行更新。
- 同步直写是缓存和数据库都写入数据后再返回。
对于读写缓存,视业务情况来使用异步写回还是同步直写,对于读写性能要求都很高的业务采用异步写回,数据都直接在缓存中操作。需要承担部分数据丢失风险,和数据不一致的风险。实际上大多数业务并没有那么高的一致性要求。
对于读多写少的场景,采用同步直写方式。只要给数据库和缓存操作加上事务控制,保证原子性。即可避免缓存一致性问题。
总结
解决缓存一致性要根据实际业务来分析,往往保持最终一致性就行,短时间内的不一致是可以接受的。提供以下方案:
采用先更新数据库再删除缓存的策略,保证数据库数据的正确。失败重试策略,延迟异步双删策略删除缓存。
更有甚者,监听数据库的变化,使用 binlog 日志异步检查数据库和缓存的一致性,不一致时,删除缓存。