技术标签: 数据库与缓存一致方案 数据库
在读取与写入缓存方面大家都是这么做的:判断是否有缓存数据,无数据的话从数据库加载,若查出数据不为null,则写入缓存,再把数据返回调用方。
但是这里有一个问题需要分析,缓存与数据库的同步,在更新完数据库后,是更新缓存还是删除缓存,还是先删缓存,再更新数据库。从理论上来说,设置过期时间是最终保持一致的解决方案。但是这不是最好的办法,在缓存有效期内或者高并发情况下,会很可能出现读取到的数据与缓存不一致的情况。
1.先更新数据库,再更新缓存
2.先删除缓存,再更新数据库
3.先更新数据库,再删除缓存
不会出现先更新缓存再更新数据库,如果更新数据库失败了,那就完了
这套方案不太好
a.线程安全角度
(1) 线程A更新了数据库
(2) 线程B更新了数据库
(3) 线程B更新了缓存
(4) 线程A更新了缓存
这样缓存中就出现了错误的数据库
b.业务场景
如果数据库写的场景比较多,读的场景比较少,就会出现数据库频繁更新,缓存频繁更新可能缓存根本就没有读,这样浪费性能
如果写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存,那么每次写入数据库后,再次计算写入缓存,也是浪费性能的。删除缓存显得更为合适。
高并发操作下,依旧会出现问题
(1) A线程进行写操作,先删除缓存
(2) B线程发现没有缓存,去数据库读取旧值
(3) B线程将旧值写入缓存
(4) A线程更新数据库
这样,数据库中的数据,又是脏数据了,还有在数据库主从分离情况下,中从没来的及同步,结果把未同步的数据写入到了缓存。这怎么解决
采用延时双删策略
redis.delKey(key);
db.update(Data);
new Thread(()->{Thread.sleep(1000);redis.delKey(key);}).start();
在不影响程序响应的情况下,开一个线程去删除缓存, 至于是多少时间后删除,可以自己评估,不一定是1s。
但是这种情况下,如果第二次删除缓存失败了怎么办?
这样也有问题
(1) 缓存刚好失效
(2) A查数据库得到一个旧值
(3) B将新值写入数据库
(4) B删除缓存
(5) A将旧值写入缓存
因为数据库的读比写快,所以这种概率比较低正常顺序应该是1 2 5 3 4,但是也是有可能发生的,也可能是查询出来值后,经过一系列计算,又写入缓存的。解决办法还是延时缓存双删,但是和2一样,删除失败了怎么办?
1.更新数据库
2.删除缓存失败
3.将要删除的key发送到消息队列
4.消费消息删除key,重试直到成功
(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。
备注说明:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。至于oracle中,博主目前不知道有没有现成中间件可以使用。另外,重试机制,博主是采用的是消息队列的方式。如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。
参考文献:
实时内容请关注微信公众号,公众号与博客同时更新:程序员星星
搜索引擎接口测试。业务接口返回6000-9000个字段搜索接口查了7个搜索链路业务接口传参加上调试信息后的接口返回43846个字段测试需求:选取某一天人物的query,标识出同时出type=12及type=3的query,计算同时出现的概率,另外把同时出的query给出来,分析使用query选取:人物top1000 跟随机1000QA测试设计:去FBI捞取top1000个人物query+任意1000个人物query。读取引擎接口,判断response返回JSONObject.
flaskio+echarts实现图表实时更新项目概述遇到的问题Websocket 概述使用WebSocket协议的原因Flask实现WebSocket(全双工通信)方法一、使用flask-sockets(经过测试,无法连接)方法二、通过 `flask-sockeio` 实现WebSocket项目概述本人毕业设计是一个基于WSN的环境监控系统,实现流程如下:硬件端采集数据(包括温湿度,光照强度,当前位置经纬度等),并通过WiFi模块,上传至后端,后端采用JAVA编写,后端接收数据并对其进行存储,同时转
lca预处理每个点的深度,以及(1<<i)层(倍增)后的祖先节点 求lca 使x在y上方即深度小于y; 把x,y跳到同一深度 找到lca 倒序枚举i向上跳的说明:首先,一个正整数可以分解成(2^i+2^(i-1)+......+2^2+2^1+2^0) 而如果可跳两个(2^i)就相当于可以跳(2^(i+1))向上跳的时候,每一次跳尽可能多的...
微服务和VUE(23): 微服务之间的调用1. 前言:开发微服务,免不了会有微服务之间的调用。在这里,我们使用的是openfeign 。因为微服务间的调用不需要通过zuul,因此就可以跳过token验证这一步,但是也没有了zuul的服务转发这个功能。为了模拟微服务间的调用,我们在my-user微服务中新建一个接口,让my-student微服务来调用这个接口。2. UserController.java 修改新建一个hello的接口,很简单,只有一个打印语句。@RequestMapping(val
本博客参考:https://blog.csdn.net/qq_33003441/article/details/809737441、指定目标设备命令如果只有一个设备/模拟器连接时,则命令为:adb <command>如果有多个设备/模拟器连接时,则需要区分指定目标设备命令,命令为:adb [-d |-e |-s <serialNumber>] <...
三维接触时间长了会慢慢发现,不同的引擎、建模软件、库对坐标系的约定各不相同。本篇总结了本人目前接触到的一些常见软件和库的坐标系,直接给出截图,希望能达到直观的速查表效果。
最近在学习自己制作CPU 需要进行再Linux系统上进行GNU工具的使用,汇总了一些自己遇到的问题和用到的指令,大部分是在别处看到的,贴贴剪剪,希望对大家有帮助。安装虚拟机大家进行百度吧,我不建议大家装个双系统,我觉得没有虚拟机好用。装机的话:推荐软件安装管家公众号进行安装设置共享文件夹:1、The command could not be located because '/us...
使用到期之后二、硬件实现1.需要的材料2.对发射模块电路进行修改3.STM32 GPIO选择三、代码实现1.载波38kHZ实现2.R05D时序实现3.调用函数并验证一、设计思路通常红外遥控采用NEC传输协议,而美的空调采用的是R05D红外协议(应该是自己设计的协议),因此用一般红外编码发射模块无法直接对空调进行控制。解决方法:获取R05D协议手册 + 用红外接收管对原有遥控器红外接收进行波形分析。一、R05D红外协议原理R05D电控功
Python2 字符编码解码 encode/decode,编码类型转换;获取编码格式
1、编写一个程序,创建一个具有26个元素的数组,并在其中存放26个小写字母,并让该程序显示该数组的内容。#include int main(void){ char c[26]; int i; char j = 'a'; for(i=0;i<26;i++,j++) { c[i] = j; printf("%c ",c[i]); } return 0;}