Redis的数据一致性

背景

我们的数据一般都会持久化到数据库,但是数据库操作很慢
Redis处理速度快,但是数据不一定是最新的
简单来说就是:数据库里的数据如果改了,Redis里面存的可能还是旧数据
这就导致了一个问题:当多线程访问系统时,如何能保证数据库和Redis的数据同步、一致呢

分析

关于双写一致性问题有两种解决思路。

  • 更新缓存
  • 删除缓存

更新缓存

  1. 如果我们先更新库,再更新缓存

假设有两个请求,都对同一条数据进行操作
A要把这条数据改为100 B要把这条数据改为200

首先请求A先把数据库中的数据改为100,刚更新完库还没来得及更新缓存,B就来把数据改为200,然后B立刻把200写入缓存,此时A再去更新缓存为100
此时缓存是100 数据库是200

  1. 如果我们先更新缓存,再更新库

前置条件同上

首先请求A先把缓存中的数据改为100,还没来得及更新数据库,B来把缓存改为200,并把数据库改为200。此时A才来更新数据库为100
此时缓存是200 数据库是100

删除缓存

此时我们考虑读写并发的场景下会有什么问题
请求A是读操作,B是写操作,要改为200
初始:缓存和数据库中数据都是100

  1. 先删除缓存,在更新库

首先B先删除缓存,此时缓存中改数据为空
A来请求数据,发现缓存中没有数据,则去数据库中查询,读取到数据为100
此时B更新数据库为200
A更新缓存的数据为自己读到的100

  1. 先更新库,再删除缓存

前置条件:此时缓存中的数据恰好过期,数据库中的值为100
A来请求数据,发现缓存中数据为空,于是去数据库中读取数据为100
B请求来更新数据库为200,接着执行删除缓存(本来就是空),这个时候B执行完了
A更新缓存数据为自己读到的100.

但是,只有满足:

  1. 缓存恰好过期
  2. B更新数据库+删除缓存的时间要比A读取数据然后写入的的时间短

第二点是极难达成的。因为更新库的操作一般是需要锁的,时间较长。但是读操作用MVCC实现,时间短。

综上。我们一般先更新库,再去删除缓存。

概率低不代表不会发生,所以我们引入一下策略:

延迟双删

就是当A操作写完缓存数据后,B通过一段延迟时间再删除一次缓存。
可以通过MQ来实现

延迟时间一般设置为多少呢?

一般根据实际数据库读操作的时间来决定

优化

而我们把删除消息投递到MQ的操作一般是写请求主动去投递的。
可以使用Canal完成该操作
Canal的使用手册