如何保证缓存与数据库双写时的数据一致性?
上一篇:别瞎写工具类了,Spring自带的这些他不香麽?
如何保证缓存与数据库双写时的数据一致性?
在做系统优化时,想到了将数据进行分级存储的思路。因为在系统中会存在一些数据,有些数据的实时性要求不高,比如一些配置信息。基本上配置了很久才会变一次。而有一些数据实时性要求非常高,比如订单和流水的数据。所以这里根据数据要求实时性不同将数据分为三级。
第1级:订单数据和支付流水数据;这两块数据对实时性和精确性要求很高,所以不添加任何缓存,读写操作将直接操作数据库。
第2级:用户相关数据;这些数据和用户相关,具有读多写少的特征,所以我们使用redis进行缓存。
第3级:支付配置信息;这些数据和用户无关,具有数据量小,频繁读,几乎不修改的特征,所以我们使用本地内存进行缓存。
但是只要使用到缓存,无论是本地内存做缓存还是使用 redis 做缓存,那么就会存在数据同步的问题,因为配置信息缓存在内存中,而内存时无法感知到数据在数据库的修改。这样就会造成数据库中的数据与缓存中数据不一致的问题。接下来就讨论一下关于保证缓存和数据库双写时的数据一致性。
解决方案
那么我们这里列出来所有策略,并且讨论他们优劣性。
先更新数据库,后更新缓存
先更新数据库,后删除缓存
先更新缓存,后更新数据库
先删除缓存,后更新数据库
先更新数据库,后更新缓存
这种场景一般是没有人使用的,主要原因是在更新缓存那一步,为什么呢?因为有的业务需求缓存中存在的值并不是直接从数据库中查出来的,有的是需要经过一系列计算来的缓存值,那么这时候后你要更新缓存的话其实代价是很高的。如果此时有大量的对数据库进行写数据的请求,但是读请求并不多,那么此时如果每次写请求都更新一下缓存,那么性能损耗是非常大的。
举个例子比如在数据库中有一个值为 1 的值,此时我们有 10 个请求对其每次加一的操作,但是这期间并没有读操作进来,如果用了先更新数据库的办法,那么此时就会有十个请求对缓存进行更新,会有大量的冷数据产生,如果我们不更新缓存而是删除缓存,那么在有读请求来的时候那么就会只更新缓存一次。
先更新缓存,后更新数据库
这一种情况应该不需要我们考虑了吧,和第一种情况是一样的。
先删除缓存,后更新数据库
该方案也会出问题,具体出现的原因如下。
此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)
请求 A 会先删除 Redis 中的数据,然后去数据库进行更新操作
此时请求 B 看到 Redis 中的数据时空的,会去数据库中查询该值,补录到 Redis 中
但是此时请求 A 并没有更新成功,或者事务还未提交
那么这时候就会产生数据库和 Redis 数据不一致的问题。如何解决呢?其实最简单的解决办法就是延时双删的策略。
但是上述的保证事务提交完以后再进行删除缓存还有一个问题,就是如果你使用的是 Mysql 的读写分离的架构的话,那么其实主从同步之间也会有时间差。
此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)
请求 A 更新操作,删除了 Redis
请求主库进行更新操作,主库与从库进行同步数据的操作
请 B 查询操作,发现 Redis 中没有数据
去从库中拿去数据
此时同步数据还未完成,拿到的数据是旧数据
此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进行查询。
先更新数据库,后删除缓存
问题:这一种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。
此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑用语言描述如下:
请求 A 先对数据库进行更新操作
在对 Redis 进行删除操作的时候发现报错,删除失败
此时将Redis 的 key 作为消息体发送到消息队列中
系统接收到消息队列发送的消息后再次对 Redis 进行删除操作
但是这个方案会有一个缺点就是会对业务代码造成大量的侵入,深深的耦合在一起,所以这时会有一个优化的方案,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。
总结
每种方案各有利弊,比如在第二种先删除缓存,后更新数据库这个方案我们最后讨论了要更新 Redis 的时候强制走主库查询就能解决问题,那么这样的操作会对业务代码进行大量的侵入,但是不需要增加的系统,不需要增加整体的服务的复杂度。最后一种方案我们最后讨论了利用订阅 binlog 日志进行搭建独立系统操作 Redis,这样的缺点其实就是增加了系统复杂度。其实每一次的选择都需要我们对于我们的业务进行评估来选择,没有一种技术是对于所有业务都通用的。没有最好的,只有最适合我们的。
感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。
· END ·
最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。
正文结束
推荐阅读 ↓↓↓
1.心态崩了!税前2万4,到手1万4,年终奖扣税方式1月1日起施行~
2.深圳一普通中学老师工资单曝光,秒杀程序员,网友:敢问是哪个学校毕业的?
3.从零开始搭建创业公司后台技术栈
4.程序员一般可以从什么平台接私活?
5.清华大学:2021 元宇宙研究报告!
6.为什么国内 996 干不过国外的 955呢?
7.这封“领导痛批95后下属”的邮件,句句扎心!
8.15张图看懂瞎忙和高效的区别!
如何保证缓存与数据库双写时的数据一致性?相关推荐
- session.merge 缓存不更新_如何保证缓存与数据库双写时的数据一致性?
在做系统优化时,想到了将数据进行分级存储的思路.因为在系统中会存在一些数据,有些数据的实时性要求不高,比如一些配置信息.基本上配置了很久才会变一次.而有一些数据实时性要求非常高,比如订单和流水的数据. ...
- java的for循环取出数据只是拿到最后一个_如何保证缓存与数据库双写的一致性...
Cache Aside Pattern 最经典的缓存+ 数据库读写的模式,就是这个Cache Aside Pattern 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时 ...
- Redis缓存穿透-热点缓存并发重建-缓存与数据库双写不一致-缓存雪崩
解决缓存问题 1.解决Redis把内存爆满的三种方法 1.1 定期删除 1.2 惰性删除 1.3 内存淘汰策略 2. 缓存穿透--缓存击穿--缓存雪崩 3. 如何解决线上缓存穿透问题 3.1 缓存击穿 ...
- 教你从0到1搭建秒杀系统-缓存与数据库双写一致
本文是秒杀系统的第四篇,我们来讨论秒杀系统中缓存热点数据的问题,进一步延伸到数据库和缓存的双写一致性问题. 在秒杀实际的业务中,一定有很多需要做缓存的场景,比如售卖的商品,包括名称,详情等.访问量很大 ...
- 掌握分布式环境缓存更新策略,提高缓存与数据库双写一致性!
概述 随着时代的发展,服务系统架构也已经由最初的单体架构转变为分布式.微服务架构模式. 从数据体量上来看,各系统存储的数据量越来越大,数据的查询性能越来越低. 此时,就需要我们不断的进行优化,最常用的 ...
- 高并发下缓存与数据库双写不一致解决方案
高并发下缓存与数据库双写不一致解决方案 参考文章: (1)高并发下缓存与数据库双写不一致解决方案 (2)https://www.cnblogs.com/wlwl/p/11601632.html (3) ...
- php双写绕过,高并发下缓存与数据库双写不一致解决方案
解决方案 在高并发场景下,数据库和缓存双写不一致情况,我们可以当写入数据库后删除缓存,当查的时候先查缓存,如果缓存为空再查数据库,最后写入缓存,但是这样还是存在一个问题. 如图所示,当出现这种情况时该 ...
- Redis缓存与数据库双写一致性
前言: 首先,缓存由于其高并发和高性能的特性,已经在项目中被广泛使用.在读取缓存方面,大家没啥疑问,都是按照下图的流程来进行业务操作. 但是在更新缓存方面,对于更新完数据库,是更新缓存 ...
- 高并发高可用复杂系统中的缓存架构(十六) 实现缓存与数据库双写一致性保障方案
再来回顾下之前的思路: 数据更新:根据唯一标识路由到一个队里中,「删除缓存 + 更新数据」 数据读取:如果不在缓存中,根据唯一标识路由到一个队里中,「读取数据 + 写入缓存」 投入队里之后,就等待结果 ...
- 读数据库遇到空就进行不下去_如何解决高并发场景下缓存+数据库双写不一致问题?...
推荐阅读: 一只Tom猫:手撕分布式技术:限流.通讯.缓存,全部一锅端走送给你!zhuanlan.zhihu.com 一只Tom猫:MySQL复习:20道常见面试题(含答案)+21条MySQL性能调 ...
最新文章
- LiveVideoStack线上分享第三季(二):对话机器人与儿童电子消费品
- 通过Main的Checkpoint Restore加快Java启动速度
- 使用Spring Data JPA进行分页和排序
- SecureCRT远程登录ubuntu
- 最小二乘法拟合多项式原理以及c++实现
- zabbix server安装配置
- GNU Radio 之 rtl-sdr
- Android Service使用需要注意事项
- Visual Leak Detector(vld)无法显示内存泄露行号
- javacv 写mp4_JavaCV 实现视频转码
- java 聊天室 私聊_Java WebSocket实现网络聊天室(群聊+私聊)
- 洛谷 P1538 迎春舞会之数字舞蹈
- python动画精灵_Python小课堂第18课:如何使用Pygame做动画精灵和碰撞检测
- 西电微机系统课程设计步进电机开环控制系统
- 公交管理系统的设计与实现
- C语言流控制命令的总结
- Socks代理是什么意思
- LintCode 183.木材加工
- 黑接骨木凝集素(SNA,EBL)
- rpm安装mysql odbc_如何以rpm方式安装mysql odbc驱动
热门文章
- Yaksa让你抛弃Adapter和ViewHolder写RecyclerView
- MySQL主从复制-基于GTID及多线程的复制
- SinoBBD:以后发优势提供不一样的一体化融合CDN服务
- 【Notification】屏蔽特定应用的通知提示
- Installshield停止操作系统进程的代码 --IS6及以上版本适用
- FlashDevelop 3.0.0 Rc2 版本下载
- 苹果mac最好用的SVN客户端:Cornerstone 4 (SVN管理工具)
- 如何在 macOS Monterey 中更改光标的颜色样式?
- 如何在 Mac 上使用快速操作工作流程?
- iOS开发之来自一线开发者的Swift学习资源推荐