更新数据时redis缓存与数据库数据不一致的问题
最初级的缓存不一致问题及解决方案
问题:先修改数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。
解决思路:先删除缓存,再修改数据库。如果数据库修改失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中。
比较复杂的数据不一致问题分析
数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了...
为什么上亿流量高并发场景下,缓存会出现这个问题?
只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题。其实如果说你的并发量很低的话,特别是读并发很低,每天访问量就 1 万次,那么很少的情况下,会出现刚才描述的那种不一致的场景。但是问题是,如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况。
解决方案如下:
更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。
一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。
如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
高并发的场景下,该解决方案要注意的问题:
- 读请求长时阻塞
由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回。
该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库。务必通过一些模拟真实的测试,看看更新数据的频率是怎样的。
另外一点,因为一个队列中,可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试,可能需要部署多个服务,每个服务分摊一些数据的更新操作。如果一个内存队列里居然会挤压 100 个商品的库存修改操作,每隔库存修改操作要耗费 10ms 去完成,那么最后一个商品的读请求,可能等待 10 * 100 = 1000ms = 1s 后,才能得到数据,这个时候就导致读请求的长时阻塞。
一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会 hang 多少时间,如果读请求在 200ms 返回,如果你计算过后,哪怕是最繁忙的时候,积压 10 个更新操作,最多等待 200ms,那还可以的。
如果一个内存队列中可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。
其实根据之前的项目经验,一般来说,数据的写频率是很低的,因此实际上正常来说,在队列中积压的更新操作应该是很少的。像这种针对读高并发、读缓存架构的项目,一般来说写请求是非常少的,每秒的 QPS 能到几百就不错了。
我们来实际粗略测算一下。
如果一秒有 500 的写操作,如果分成 5 个时间片,每 200ms 就 100 个写操作,放到 20 个内存队列中,每个内存队列,可能就积压 5 个写操作。每个写操作性能测试后,一般是在 20ms 左右就完成,那么针对每个内存队列的数据的读请求,也就最多 hang 一会儿,200ms 以内肯定能返回了。
经过刚才简单的测算,我们知道,单机支撑的写 QPS 在几百是没问题的,如果写 QPS 扩大了 10 倍,那么就扩容机器,扩容 10 倍的机器,每个机器 20 个队列。
- 读请求并发量过高
这里还必须做好压力测试,确保恰巧碰上上述情况的时候,还有一个风险,就是突然间大量读请求会在几十毫秒的延时 hang 在服务上,看服务能不能扛的住,需要多少机器才能扛住最大的极限情况的峰值。
但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了,然后那些数据对应的读请求过来,并发量应该也不会特别大。
- 多服务实例部署的请求路由
可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,都通过 Nginx 服务器路由到相同的服务实例上。
比如说,对同一个商品的读写请求,全部路由到同一台机器上。可以自己去做服务间的按照某个请求参数的 hash 路由,也可以用 Nginx 的 hash 路由功能等等。
- 热点商品的路由问题,导致请求的倾斜
万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能会造成某台机器的压力过大。就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以其实要根据业务系统去看,如果更新频率不是太高的话,这个问题的影响并不是特别大,但是的确可能某些机器的负载会高一些。
更新数据时redis缓存与数据库数据不一致的问题相关推荐
- redis缓存跟数据库数据不一致问题解决
如何保证缓存与数据库的双写一致性? 最经典的缓存+数据库读写的模式. 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应. 更新的时候,先删除缓存,再更新数据库. 为什 ...
- 在Oracle中IMP导入数据时,如何解决目标数据库字符不一致问题
生产环境中经常使用到Oracle的IMP导入和EXP导出来功能来达到数据迁移的目的,通常在源数据库和目标数据库中查询字符集是否致, 测试环境中导入IMP导入报错信息如下: 导入命令如下: [oracl ...
- Redis 缓存和 MySQL 数据如何实现一致性?
需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库. 这个业务场景,主要 ...
- Spring Boot实战解决高并发数据入库: Redis 缓存+MySQL 批量入库
前言 最近在做阅读类的业务,需要记录用户的PV,UV: 项目状况:前期尝试业务阶段: 特点: 快速实现(不需要做太重,满足初期推广运营即可) 快速投入市场去运营 收集用户的原始数据,三要素: 谁 在什 ...
- react与mobx搭配时如何缓存mobx的数据(刷新之后保存状态)
react与mobx搭配时如何缓存mobx的数据(刷新之后保存状态) 问题 思路 代码 写在最后 问题 近日在使用react的过程中,在状态管理方面,我使用了mobx,但是发现它与vuex有一个一样的 ...
- 链接mysql_使用python链接mysql及redis(缓存型数据库)
python链接数据库mysql操作,首先我们需要借助第三方库pymysql. cmd终端下载:pip install pymysql import pymysql#链接数据库db = pymysql ...
- 使用hibernate的this.getSession().createSQLQuery(sql).list();方法查询数据时出现查到的数据和想象的不一致,很是郁闷,诡异...
今天 使用hibernate的this.getSession().createSQLQuery(sql).list();方法查询数据时出现查到的数据和想象的不一致的问题,郁闷我很长一段时间 执行的方法 ...
- mysql中用于删除数据的是什么意思_在删除关系表中数据时,要先删除主表数据再删除从表数据。_学小易找答案...
[单选题]在MySQL中,所有存储过程的信息存放在下列哪个数据库中 [填空题]拉伸试验能测定的塑性指标有: 和 . [单选题]下面查看视图的基本信息的语句中,正确的是 [单选题]下面关于视图建立的说法 ...
- navicat还原mysql备份数据时,部分表无数据
最近在使用navicat正在本地恢复数据时,发现部分表数据,百度一下,发现是由于max_allowed_packed设置过小,而insert时数据过多导致出错,修改max_allowed_packed ...
最新文章
- NFV — 系统架构
- linux服务器ftp上传文件为空,Linux 服务器 ftp上传文件出现的问题
- Vue学习(增删改查、ES6模块化概念)-学习笔记
- 车流检测之halcon光流法算法实现
- 使用SpringMVC参数传递时,解决get请求时中文乱码的问题
- IoT -- (三) 2018 Top物联网项目排名
- Linux使用CLASS_ATTR创建节点
- mysql同音字搜索_React-Native 给客户端来个「同音词模糊搜索」
- JAVA入门学习 —数组排序的方法
- 最全的react视频【黑马程序员】--第一章 react介绍
- 9 Creating a Chain of Effects
- 计算机的内存大小有何作用,电脑内存用处有多大?你可能想不到!
- windows和ubuntu双系统之双硬盘安装
- 在职阿里3年,一个27岁女软件测试工程师的心声
- IBM DS3400换电池后报错Individual Drive –Degraded path
- IPP与Opencv配合使用
- oracle 11g失败,求助,oracle 11g 启动失败,求大神帮忙看看,谢谢
- 线程的故事(了解线程生命周期)
- 广东省计算机二级c语言真题,广东省计算机二级考试题及答案
- 嵌入式linux使用A7680C 4G 网卡
热门文章
- 什么是LOST.DIR?
- I2C协议研读(九):十位寻址
- classes是什么意思怎么读_class是什么意思_class怎么读_class翻译_用法_发音_词组_同反义词_班-新东方在线英语词典...
- 【TI-AM5728】(1)开发环境搭建
- 如何表格合并快速简单?
- python实时检测键盘输入函数
- 前端性能优化之WebP图片
- ajax异步请求中途取消
- 大数据背景下的智慧物流:物流行业解决方案
- 偏门赚钱日赚1000,这个创业项目我本来不想说的....