数据库和缓存如何保持一致性
2022/4/2 2:19:30
本文主要是介绍数据库和缓存如何保持一致性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
数据库和缓存如何保持一致性
问题引入
大量的访问请求使得数据库操作频繁,结果导致服务器性能下降,为了解决该问题可以引入reids
,让其作为数据库的缓存。这样,在客户端请求数据时,能从缓存中读取就可以不必去数据库中读取,从而减轻数据库压力,提高服务器性能。但是如果数据发生变化,而数据又存在于数据库和redis
中,此时就会产生数据一致性问题。
方案一:先更新数据库,再更新缓存
首先结论是该方案不能解决数据一致性问题,原因出在并发上。比如现在有 A 请求和 B 请求,这两个请求同时更新同一条数据,此时如果出现如下顺序:
- A 请求先将数据库数据更新为 1
- B 请求将数据库数据更新为 2
- B 请求将缓存更新为 2
- A 请求将缓存更新为 1
此时,数据库中的数据为 2,而缓存中数据为 1,出现数据不一致的问题,所以该方案不成立。
方案二: 先更新缓存,再更新数据库
该方案依然不成立,道理如方案一种的案例一样,只不过将数据库和缓存换一下位置,最后依然会出现数据不一致。
旁路缓存策略(Cache Aside)
不更新缓存,而是删除缓存中的数据,然后,到读取数据时,发现缓存中没有数据后再去数据库中读取数据并更新到缓存中。
此时又引申出一个问题,是先删除缓存再更新数据库还是先更新数据库再删除缓存。
方案三: 先删除缓存,再更新数据库
结论是改方案不成立。假设有一用户年龄为20, A 请求更新该用户年龄为21,B 请求读取该用户年龄,此时出现如下顺序:
- A 请求删除缓存
- B 请求缓存未命中,读取数据库中年龄为 20,并将 20 更新到缓存中
- A 请求更新数据库中年龄为 21
此时,数据库中年龄为 21,而缓存中数据为 20,出现数据不一致问题。对于此情况有如下解决方案:
# 删除缓存 redis.delKey(X) # 更新数据库 db.update(X) # 睡眠 Thread.sleep(N) # 再删除缓存 redis.delKey(X)
睡眠操作主要是为了确保 A 请求在睡眠的时候, B 请求能够在这一段时间完成所有操作。最后再删掉缓存数据,所以 A 请求的睡眠时间要大于 B 请求从数据库读数据+写入缓存
的时间。但是具体睡眠多久是个玄学问题,很难评估出来,所以这个方案也只是尽可能保证数据一致性而已,还是会有数据不一致的问题,因此更建议使用方案四。
方案四: 先更新数据库,再删除缓存
假设开始年龄数据在缓存中不存在,A 请求读取年龄,B 请求更新年龄,此时出现如下顺序:
- A 请求缓存未命中,读取数据库中年龄为 20
- B 请求更新数据库年龄为 21
- B 请求删除缓存
- A 请求将 20 写入缓存
此时,缓存中数据为 20, 而数据库中数据为 21,出现数据不一致问题。但是在实际中,这个问题出现的概率并不高,因为缓存的写入通常要远远快于数据库的写入,所以认为该方案是可以保证数据一致性的。
同时也可以给缓存加上过期时间,万一出现数据不一致,一旦数据过期被移除缓存,之后请求数据就需要从数据库中取数据,然后把数据同步到缓存中进而保持数据一致性。
但是此时还存在其他问题,即删除缓存的操作不一定成功,这就会导致客户端得到的是未更新的数据。现在有如下两种方案解决这个问题。
1. 重试机制
引入消息队列,将要从缓存中删除的数据加入到消息队列中。
- 如果应用删除缓存失败,就从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。如果重试超过一定次数还是没有成功,就需要向业务层发送报错信息了。
- 如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作。
2. 订阅 MySQL binlog,再操作缓存
在数据库更新成功后就会产生一条变更日志,记录在 binlog 中。于是可以通过订阅 binlog 日志,拿到具体要操作的数据,然后再执行缓存删除。阿里巴巴的 Canal 中间件就是基于这个实现。
Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后就会开始推送 binlog 给 Canal,Canal 解析 binlog 字节流后,转换为便于读取的结构化数据,供下游订阅使用。
补充
如果在业务中对缓存命中率有很高的要求,此时就需要更新数据库+更新缓存
的方案。为了解决数据不一致问题,现有提供如下两种做法:
- 在更新缓存前先加个分布式锁,保证同一时间只有一个请求更新数据库和缓存,就不会产生并发问题了,但引入锁会对写入性能带来影响。
- 在更新完缓存时,给缓存加上较短的过期时间,这样即使出现缓存不一致问题,缓存的数据也会很快过期,对业务还是可以接受的
这篇关于数据库和缓存如何保持一致性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略