Redis 缓存不一致 - 延迟双删
概述
Redis 缓存不一致是指实际存储在数据库里的数据和缓存在Redis的数据不一致。而导致这个问题的原因是数据更新所导致的。只有数据发生了更新才有可能导致存储在两个位置(DB和Redis)的数据不一致。那么是不是只有我们在更新数据的时候,同时更新数据库和Redis就可以了呢?看似简单直接的方案往往藏了很多坑。因为这两个数据源的更新并不是原子性的,在并发情况下有诸多问题。为了简化对多数据源的管理,一般的方案都是选择更新数据库而选择让删除缓存使缓存失效。这样在获取数据的时候,会自动去数据库拉取最新的数据。
那么是不是我们只要选择更新数据,删除Redis缓存就可以实现缓存一致呢?
如何删除缓存
更新数据的时候使缓存失效,从而在需要数据的时候重新从数据库获取最新的数据,来刷新缓存达到缓存一致的目的。这个思路是对的。那么我们应该在什么时候去删除缓存呢?在更新数据库之前还是更新数据库数据之后呢?朴素的直觉告诉我们,肯定是在更新完数据库之后再来删除缓存即可。
更新后删除缓存
我们在更新数据库成功之后,再删除缓存。因为两个操作不是原子性的,所以在两个操作之间读取数据的线程依旧会短暂的读取到旧的缓存数据,然而这并不是什么太大的问题。在并发的极端情况下甚至会出现脏数据。
并发更新下的脏数据
当多个线程同时同时更新缓存,同时存在读取数据的线程。每一个更新的操作都可能穿插执行。在某些情况下会生成脏数据。
延迟删除
生成脏数据的原因是因为在并发情况下,读取数据数据的操作发生在更新数据库数据之前,而写缓存的操作却发生在删除缓存之后。所以我们可以延迟删除缓存操作,将缓存删除的操作延迟到脏数据写缓存之后。这样就可以删除缓存脏数据。延迟删除虽然可以缓解在并发情况下对脏数据的清理。但是也极大的拉长了缓存数据的更新时间。
延迟双删
我们可以在更新前就删除数据,可以极大的减少缓存数据的延迟。更新前就删除数据,只要数据读取不是发生在更新数据库之前,依旧可以读取到最新的数据。
当出现极端情况下的脏数据时,延迟删除可以补偿对脏数据的删除。从而达到最终的数据一致性。
总结
综合而言,我们可以通过延迟双删来解决在并发情况导致的缓存数据不一致的问题
- 删除缓存
- 更新数据库
- 延迟等待一段时间
- 重新删除缓存
归根结底,该问题的本质其实就是并发问题。如果我们再极端假设,延迟删除依旧会发生在写数据之前(在多副本的情况下概率更大)。还是会导致数据不一致的问题。我们可以通过加分布式锁来解决问题。