目录
缓存一致性问题概念
缓存一致性问题指的是缓存中的数据和数据库中的数据不一致的情况,这在分布式系统中尤其常见。缓存一致性问题可能导致用户读取到旧数据或者错误数据,从而影响系统的可靠性和用户体验.
缓存一致性问题的成因
-
缓存更新不及时:
- 当数据库中的数据发生变化后,如果缓存中的数据没有及时更新,缓存和数据库的数据就会不一致。
-
缓存与数据库更新的顺序问题:
- 在进行数据写操作时,如果先更新数据库再更新缓存,或者先更新缓存再更新数据库,都有可能导致在并发情况下出现数据不一致的问题。
-
分布式系统的并发问题:
- 在分布式系统中,多个节点可能同时读取和写入同一条数据,导致不同节点缓存的数据与数据库中的数据不一致。
-
缓存淘汰策略:
- 缓存中存储的数据有时会因为设置的过期时间或内存空间不足而被淘汰。如果被淘汰的数据还没有过期或者没有重新加载,就可能导致数据不一致。
缓存一致性问题的解决方案
旁路设计模式中同步删除
核心流程
存在问题
如图所示,更新请求进来,先更新数据,再删除缓存。但是会导致以下问题
- 如果删除缓存失败,会导致脏数据
- 在并发场景下,会导致数据不一致问题
写线程A | 读线程B |
删除缓存 | |
读取缓存,未命中,查询数据S1 | |
更新请求进来,更新数据库S1->S2 | |
删除缓存 | |
将S1放入缓存 |
在上图所示:
- 读线程B读取缓存,缓存中不存在,去查询数据库,查询到S1
- 写线程A更新请求进来,更新数据库S1->S2,然后删除了缓存
- 读线程B将S1放入缓存中。
- 导致了数据库数据为S2,缓存中数据为S1.
延迟双删(面试常问)
核心流程
延迟双删策略主要分为以下几个步骤:
-
更新数据库:
- 首先,应用程序更新数据库中的数据。此时,缓存中的数据可能仍然是旧的。
-
第一次删除缓存:
- 紧接着,删除缓存中与数据库中更新的数据对应的缓存条目。此操作确保下次读取数据时会从数据库中读取最新数据。
-
延迟一段时间:
- 设置一个合理的延迟时间。这个延迟时间通常是根据系统的具体需求和网络延迟、数据库响应时间等因素来确定的。目的是等待所有可能的并发读请求完成。
-
第二次删除缓存:
- 在延迟时间结束后,再次删除缓存中的数据。这一步是为了处理可能在第一次删除缓存与数据库更新之间发生的并发写入或读取操作。
//延迟双删伪代码
public void updateData(String key, String newValue) {
// 1. 更新数据库中的数据
db.update(key, newValue);
// 2. 删除缓存中的旧数据
redis.del(key);
// 3. 延迟一段时间
try {
Thread.sleep(500); // 延迟500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 4. 再次删除缓存(以防止并发问题)
redis.del(key);
}
延迟双删的应用场景
-
高并发场景:
- 在高并发环境下,多个线程可能同时读写同一数据。延迟双删可以减少因并发操作导致的缓存不一致问题。
-
需要强一致性的场景:
- 如果业务逻辑要求缓存中的数据与数据库中的数据必须保持一致(即便延迟一点时间),延迟双删是一种有效的策略。
优点:
-
减少并发问题:通过延迟第二次删除缓存,降低了在更新数据库和缓存期间发生的并发问题,从而提高了缓存和数据库的一致性。
-
简单易实现:延迟双删的实现逻辑相对简单,只需在代码中加入一些延迟处理即可。
缺点:
-
延迟时间设置复杂:延迟时间的选择需要非常慎重。延迟时间太短可能无法解决并发问题,太长又可能影响系统性能。
-
仍然存在短暂的不一致性:在第一次删除缓存与数据库更新之间,仍可能出现短暂的不一致性。
-
多次删除操作开销:两次删除操作增加了缓存的开销,特别是在高频读写的场景下,可能对缓存性能产生一定影响。
监听binlog删除+重试(常用)
核心流程
监听 Binlog 并删除缓存的流程
-
开启 MySQL Binlog:
- 首先,确保 MySQL 的 Binlog 功能已经开启,并且配置了适当的 Binlog 格式(如
ROW
格式,能更精确地记录行级别的变化)。
- 首先,确保 MySQL 的 Binlog 功能已经开启,并且配置了适当的 Binlog 格式(如
-
监听 Binlog 日志:
- 使用工具或程序(如 Debezium、Canal、Maxwell 等)监听 MySQL 的 Binlog 日志,实时捕获数据库的变化事件。
-
解析 Binlog 事件:
- 解析 Binlog 日志中的事件,识别出需要处理的表和行级数据变更(如 INSERT、UPDATE、DELETE 操作)。
- 推送消息到消息队列
-
删除对应的缓存:
- 根据解析出的变更信息,定位需要删除的缓存条目。通常是根据变更的主键或其他唯一键来确定缓存键。
-
重试机制:
- 如果删除缓存操作失败(如由于网络问题或 Redis 服务不可用),应触发重试机制,确保缓存删除成功。
优点:
-
实时性强:通过监听 Binlog,可以实时捕获数据库的变化并更新缓存,确保缓存数据的新鲜度和一致性。
-
可靠性高:通过重试机制,即使缓存删除失败,也能够在重试后成功删除,确保系统的稳定性。
-
适应性广:这种方式可以适用于各种数据库变更,不需要在应用程序层面增加复杂的逻辑。
缺点:
-
实现复杂度高:需要引入外部工具(如 Canal),并且解析 Binlog 和维护重试机制的实现较为复杂。
-
性能开销:监听 Binlog 并频繁操作缓存会增加系统的性能开销,尤其是在高并发环境下。
-
延迟问题:尽管 Binlog 监听具有实时性,但实际操作中可能会有少量延迟,尤其是在高负载下。
缓存一致性问题是分布式系统中的一个复杂问题,需要根据具体的业务需求和场景选择合适的解决方案。通过合理的缓存策略和一致性保证,可以有效提高系统的性能和可靠性。