思考问题
我们将数据同时保存在了 数据库 和 Redis 当中,如果数据库中的数据更新了,但 Redis 缓存中的数据没有更新。
当用户来访问数据的时候,最先访问的 Redis 缓存中的旧数据,这就导致与数据库的数据不一致!
这就需要我们使用 Redis 的缓存更新策略。
缓存更新策略,有哪些?
-
内存淘汰
-
使用 Redis 的内存淘汰机制,当内存中的数据越来越多,没有进行删除 或者 漏删除了,导致内存中的数据超过了设定的最大内存限制时,Redis 会启动开启内存淘汰机制,释放部分的数据。
-
一致性差,只有当数据超过了设定的最大内存限制才会释放部分的数据。
-
-
超时剔除
-
将数据缓存到 Redis 中的同时给数据设置过期时间,到期后自动删除 Redis 缓存中的数据。
-
一致性一般,如果设置的过期时间过长,在过期时间之内数据更新了,也会导致 Redis 缓存中的数据是旧数据。
-
-
主动更新
-
自己编写代码逻辑,主动的去更新数据,具体介绍和内容,往下看......
-
一致性好。
-
主动更新
初始引入 Redis 缓存
我们现在是将数据保存到了 数据库 和 Reids 中 [如下图]
思考问题
如果数据库中的数据更新了,但 Redis 未更新,就会出现数据不一致。
虽然上面提到了内存淘汰和超时剔除,但是还会存在一致性问题,所以需要让我们主动去更新缓存。
主动更新缓存
更新缓存+更新数据库
因为这是两个步骤,就可能引发第一个步骤成功第二个步骤失败的问题、同时成功但出现了并发的问题。
-
先更新缓存,后更新数据库?
如果缓存更新成功,但数据库更新失败,那么就会出现:缓存中是新数据,数据库中是旧数据。
当缓存的过期时间到了,才会读取数据库中的旧数据。
-
先更新数据库,后更新?
如果数据库更新成功,但缓存更新失败,那么就会出现:数据库中是新数据,缓存中是旧数据。
当缓存的过期时间到了,才会读取数据库中的新数据。
-
更新数据库和更新缓存同时成功,但加入了并发。
两个线程同时去更新 Redis 和 数据库,虽然更新 Redis 数据时,线程-A 先于 线程-B,但是更新数据库数据时,线程-B 先于 线程-A 的,导致 Redis 中的数据 与 数据库中的数据不一致。 [如下图]
说明:先执行【1】再执行【2】
所以更新缓存+更新数据库,这种方案不行,换一个方案!
更新数据库+删除缓存
用户读取数据时会先到 Redis 缓存中的读取,缓存中没有再去 数据库中读取,读取成功后会写入 Redis 缓存中。
所以我们可以更新数据库数据后,删除掉缓存中的数据,等到再次读取该数据的时候由数据库写入到缓存中。
这也是两个步骤,就可能引发第一个步骤成功第二个步骤失败的问题、同时成功但出现了并发的问题。
-
先删除缓存,后更新数据库?
如果缓存删除成功,但数据库更新失败,那么就会出现:缓存中没有数据,数据库中是旧数据。
当用户再次获取数据时,直接读取数据库中的旧数据,然后又写入到缓存中。
-
先更新数据库,后删除缓存?
如果数据库更新成功,但缓存删除失败,那么就会出现:数据库中是新数据,缓存中是旧数据。
只有缓存的过期时间到了,才会读取数据库中的新数据,否则会一直读取缓存中的旧数据。
-
先删除缓存,后更新数据库,这两步同时成功,但加入了并发。
两个线程:线程-A 修改数据、线程-B 读取数据
因为 线程-A 删除了缓存,线程-B 中途读取数据只能从数据库中读取,线程-A 写入新数据后,线程-B 将旧数据写入到缓存中。
-
先更新数据库,后删除缓存,这两步同时成功,但加入了并发。
但是,出现这种问题必须满足 3 个条件:
-
缓存失效
-
读取数据和更新数据,有了并发
-
更新数据库+删除缓存 所花费的时间 比 读取数据库+写入缓存 所花费的时间 短
第 3 个条件发送的概率极低,因为写数据库一般会"加锁",所花费的时间会比读取数据库时间长。
所以,选择"先更新数据库,后删除缓存",因为这种方案出现数据不一致的概率极低。
-