关于redis的几件小事(八)缓存与数据库双写时的数据一致性
1.Cache aside pattern
这是最经典的 缓存+数据库 读写模式,操作如下:
①读的时候,先读缓存,缓存没有就读数据库,然后将取出的数据放到缓存,同时返回请求响应。
②更新的时候,先删除缓存,然后更新数据库。
2.为什么是删除缓存,而不是更新缓存呢?
①因为很多时候,缓存中放的并不是简单的从数据中取出来值,可能要进行一个状态的替换,一些数据的计算,还有可能要进行数据的组合等等。
②二八法则,即20%的数据,占用了80%的访问量,这个更新的数据,可能是冷门数据,好久也访问不了不了一次,这样只会占用缓存内存。lazy思想,数据等你第一次要的时候再去加载,避免没必要的内存和时间浪费。
3.最初级的缓存不一致问题以及解决方案
问题 :先修改数据库,再删除缓存,如果缓存删除失败了,那么就会早上数据库和缓存数据不一致。
解决思路 :先删除缓存,再修改数据库,如果删除缓存成功了,如果修改数据库失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中
4.比较复杂的数据不一致问题分析
问题 :数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。
一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。数据变更的程序完成了数据库的修改。
这样就又导致了数据不一致
5.为什么上亿流量高并发场景下,缓存会出现这个问题?
因为只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题。
6.数据库与缓存更新与读取操作进行异步串行化
为了解决上面的并发读写问题,可以考虑将更新和读取操作进行串行化。
①更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列里面去。
②读取数据的时候,如果发现缓存中没有,那么将从数据库读取数据的操作和更新缓存的操作一起路由到同一个JVM内部的队列中去。
③一个队列对应一个工作线程,然后线程从队列里面去取请求进行操作。
这样就可以将同一个key的操作进行串行化,一个数据变更的操作,先执行,删除缓存,然后再去更新数据库,但是还没完成更新,此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。
如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回; 如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
7.高并发的场景下,该解决方案要注意的问题
①读请求长时间阻塞问题
由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回。
该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库。
务必通过一些模拟真实的测试,看看更新数据的频繁是怎样的。
②读请求并发量过高
还有一个风险,就是突然间大量读请求会在几十毫秒的延时hang在服务上,看服务能不能抗的住,需要多少机器才能抗住最大的极限情况的峰值。
但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了,然后那些数据对应的读请求过来,并发量应该也不会特别大。
③多服务实例部署的请求路由
可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,都通过nginx服务器路由到相同的服务实例上。
④热点商品的路由问题。导致请求的倾斜
万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能造成某台机器的压力过大。
就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是特别大。
但是的确可能某些机器的负载会高一些。
关于redis的几件小事(八)缓存与数据库双写时的数据一致性的更多相关文章
- Redis使用总结(二、缓存和数据库双写一致性问题)
首先,缓存由于其高并发和高性能的特性,已经在项目中被广泛使用.在读取缓存方面,大家没啥疑问,都是按照下图的流程来进行业务操作. 但是在更新缓存方面,对于更新完数据库,是更新缓存呢,还是删除缓存.又或者 ...
- 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题
今天来分享一下Redis几道常见的面试题: 如何解决缓存雪崩? 如何解决缓存穿透? 如何保证缓存与数据库双写时一致的问题? 一.缓存雪崩 1.1什么是缓存雪崩? 回顾一下我们为什么要用缓存(Redis ...
- (转)面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题
背景:redis问题在面试过程中经常被问到,对于常见问题一定不能放过. 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题 一.缓存雪崩 1.1什么是缓存雪崩? 如果缓存数据设置的过 ...
- 关于redis的几件小事(一)redis的使用目的与问题
1.redis是用来干嘛的? Redis is an open source (BSD licensed), in-memory data structure store, used as a dat ...
- K:缓存数据库双写数据一致性方案
对于缓存和数据库双写,其存在着数据一致性的问题.对于数据一致性要求较高的业务场景,我们通常会选择使用分布式事务(2pc.paxos等)来保证缓存与数据库之间的数据强一致性,但分布式事务的复杂性与对资源 ...
- 关于redis的几件小事(七)redis缓存雪崩与穿透
1.缓存雪崩 (1)什么是缓存雪崩 缓存雪崩指的是在同一时刻,缓存大量失效,导致大量的请求直接到了数据库,数据库压力剧增,引起系统崩溃.可能出现的情况有: ①大量的key设置了相同的过期时间,导致在缓 ...
- 关于redis的几件小事(六)redis的持久化
1.redis持久化的意义 redis持久化的意义,在于 故障恢复 . 如果没有对数据进行持久化,那么如果redis遇到灾难性的故障,就会丢失所有的数据. 如果通过redis的持久化机制将数据持久化到 ...
- 关于redis的几件小事(四)redis的过期策略以及内存淘汰机制
1.数据为什么会过期? 首先,要明白redis是用来做数据缓存的,不是用来做数据存储的(当然也可以当数据库用),所以数据时候过期的,过期的数据就不见了,过期主要有两种情况, ①在设置缓存数据时制定了过 ...
- 关于redis的几件小事(三)redis的数据类型与使用场景
1.string 这是最基本的类型了,就是普通的set和get,做简单的kv缓存. 2.hash 这个是类似map的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对 ...
随机推荐
- LeetCode 42. 接雨水(Trapping Rain Water)
题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况 ...
- Ngrinder 源码之Maven 项目
Ngrinder支持Maven结构的测试脚本.使用ScriptHandlerFactory来个脚本选择处理器handler,目前有JythonScriptHandler, GroovyScriptHa ...
- redux异步
在一个项目中 redux 是必不可少的,redux 中没有提供异步的操作,但是异步又是项目开发中重要的一部分,所以我们的 redux 对此有进行了拓展: 所以我们需要 redux-thunk 的插件, ...
- JSP——常用配置-获取项目跟路径
将该内容放入一个空白的JSP文件中,保存为basepath.jsp,然后在需要引用的JSP文件中使用include标签引用. <% String path = request.getContex ...
- Host x.x.x.x not found in /root/.ssh/known_hosts
候解决办法是,只要找到电脑里“.ssh” 文件夹,将文件夹里的文件”known_hosts”删除掉或者担心删除了会有风险,改个名字,然后在重新提交的时候,就能正确提交了 将known_hosts删掉或 ...
- CSV Data Set Config参数说明
以下是CSV Data Set Config各个参数的简要说明: FileName:即同目录下csv文件的名称 File Encoding: 默认为ANSI Varible Names: 定义文本文件 ...
- Ubunut16.04 安装 Theano+GPU
1. 更新NVIDIA显卡驱动 安装好系统之后首先在系统更新管理器中更新显卡驱动,如下图 点击Apply Changes 2. 安装numpy,scipy,theano pip安装即可 sudo ...
- nohup sh start.sh >/dev/null 2>&1 &
nohup sh start.sh >/dev/null 2>&1 & 背景说明 start.sh 脚本里,写了Java应用程序启动的相关命令,并且在 log4j.prop ...
- python定位隐藏元素
定位隐藏要素的原理: 页面主要通过“display:none”来控制元素不可见.所以我们需要通过javaScript修改display的值得值为display="block,来实现元素定位的 ...
- C#编程 线程,任务和同步(2) 开启线程
创建线程的几种方法: 1 异步委托 创建线程的一种简单方式是定义一个委托,并异步调用它. 委托是方法的类型安全的引用.Delegate类 还支持异步地调用方法.在后台,Delegate类会创建一个执行 ...