如何保证Redis与数据库的一致性

本文深入探讨了三种常见的缓存更新策略:缓存失效(Cache Aside)、读/写透明(Read/Write Through)和写回调(Write Back)。分析了每种策略的工作原理,存在的问题及适用场景,帮助读者理解如何在不同情况下选择合适的缓存策略。

目录

先上一个错误操作示范

正确姿势

一、缓存失效(Cache Aside Pattern)

策略(Facebook论文范例):

问题:

二、读/写透明(Read/Write Through Pattern)

策略:

三、写回调( Write Back)

策略:


先上一个错误操作示范

操作方法:先删除缓存,然后再更新数据库,而后续的操作会把数据再写入到的缓存中。

存在的问题:查询请求没hit到缓存会把旧数据更新到缓存中。

解释:两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。

正确姿势

一、缓存失效(Cache Aside Pattern)

策略(Facebook论文范例)

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

问题:

1、为什么不写完数据库后再更新缓存

并发两个写操作可能会导致脏数据。

2、该策略存在什么问题?

问题1 脏读:一个查询缓存请求,一个更新缓存请求。查询缓存没有hit,从数据库查出了旧数据,此时有个更新请求过来,更新数据库后把缓存设为了失效。刚才查询请求此时把旧数据更新到缓存中,于是缓存中的数据会一直是脏数据。

问题2 更新缓存失效失败:概览太低不考虑

解释:实际发生的概览非常低,需满足两个条件:1、读缓存时缓存失效,而且并发着有一个写操作 2、读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存。由于一般写操作时间远大于读操作时间,所以发生概率很低。要么通过2PC(太慢)或是Paxos(太复杂)协议保证一致性,要么就是拼命的降低并发时脏数据的概率。

二、读/写透明(Read/Write Through Pattern

策略:

Read Through

Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

Write Through

Write Through 套路和Read Through相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

三、写回调( Write Back)

策略:

在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。

优点:让数据的I/O操作飞快无比。

缺点:数据不是强一致性的,而且可能会丢失。

https://blog.youkuaiyun.com/weixin_45480785/article/details/118253615

https://www.cnblogs.com/lgx211/p/18496124

https://developer.jdcloud.com/article/3226

参考

1、缓存更新的套路:

https://coolshell.cn/articles/17416.html#Write_Behind_Caching_Pattern

在现代Web应用中,Redis常被用作高性能缓存层,以提升系统响应速度降低数据库访问压力。然而,Redis后端数据库之间的数据一致性问题也随之而来,成为系统设计中的关键挑战之一。以下是几种常见的保证Redis数据库一致性的方法: ### 1. 缓存更新策略 缓存更新策略通常包括以下几种方式: - **先更新数据库,再更新缓存**:这种策略适用于写操作较多的场景。首先更新数据库,然后更新Redis缓存确保缓存中的数据始终数据库保持一致。但这种方式在高并发环境下,可能会出现中间状态不一致的问题。 - **先更新数据库,再删除缓存**:这是一种更常见的方式。当数据发生变化时,先更新数据库,然后删除Redis中的缓存。下次读取时,缓存会重新从数据库中加载最新数据。这种方式可以避免缓存数据库之间的不一致,但可能会导致缓存穿透问题。 - **先删除缓存,再更新数据库**(延迟双删):在写操作时,先删除缓存,然后更新数据库。为了防止并发读取旧数据,可以在更新完数据库后再次删除缓存确保缓存中不会保留旧值。 ### 2. 使用事务命令队列 Redis支持事务机制,可以将多个命令打包执行,确保这些命令在执行过程中不会被其他客户端的请求打断。这种方式可以用于保证缓存操作数据库操作的原子性,但需要结合数据库事务一起使用才能真正实现一致性[^1]。 ### 3. 持久化复制机制 Redis提供了RDB(快照)AOF(追加日志)两种持久化机制,可以将内存中的数据保存到磁盘中。此外,Redis的主从复制机制可以将数据从一个Redis节点复制到多个节点,提升数据的可用性一致性。这些机制虽然主要用于Redis自身的高可用,但在一定程度上也支持后端数据库的一致性保障[^1]。 ### 4. 分布式锁机制 在分布式系统中,为避免多个服务同时更新缓存数据库导致数据不一致,可以使用分布式锁机制来控制对共享资源的访问。例如,使用Redis实现的分布式锁,可以确保在某一时刻只有一个服务能够执行更新操作。然而,加锁会引入额外的性能开销,因此在实际应用中需权衡性能一致性需求[^4]。 ### 5. 异步更新后台同步 在某些场景下,可以采用异步更新的方式,将Redis中的写操作异步同步到数据库中。例如,Redis接收到更新请求后立即返回成功,后台任务负责将数据持久化到数据库。这种方式可以提升写入性能,但存在数据丢失风险,适用于对一致性要求不高的场景[^5]。 ### 6. 事件驱动消息队列 通过引入消息队列(如Kafka、RabbitMQ等),可以将数据库的更新事件发布到队列中,由Redis消费者监听并更新缓存。这种方式可以实现松耦合的系统架构,并保证数据最终一致性。 ### 示例代码:先更新数据库,再删除缓存 ```python def update_data(key, new_value): # 1. 更新数据库 db.update(key, new_value) # 2. 删除Redis缓存 redis.delete(key) ``` 上述代码展示了在更新数据库后删除缓存的基本流程,确保下次读取时缓存会重新加载最新数据。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值