如何优化网站标题/站长工具果冻传媒
文章目录
- 概述
- 示例
- 缓存双写一致性
- 缓存按照操作来分,细分2种
- 读写缓存:同步直写策略
- 读写缓存:异步缓写策略
- 双检加锁策略
- 数据库和缓存一致性更新策略
- 先更新数据库,再更新缓存
- 先更新缓存,再更新数据库
- 先删除缓存,再更新数据库
- 解决方案:延时双删策略
- 先更新数据库,再删除缓存
- 解决方案
- 总结
- 问题示例
概述
示例
缓存双写一致性
- 如果redis中有数据
需要和数据库中的值相同- 如果redis中无数据
数据库中的值要是最新值,且准备回写redis
缓存按照操作来分,细分2种
- 只读缓存
- 读写缓存
读写缓存:同步直写策略
- 写数据库后也同步写redis缓存,缓存和数据库中的数据一致;
- 对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略
读写缓存:异步缓写策略
- 正常业务运行中,mysql数据变动了,但是可以在业务上容许出现一定时间后才作用于redis,比如仓库、物流系统
- 异常情况出现,不得不将失败的动作重新修补,有可能需要借助kafka或者RabbitMQ等消息中间件实现重试重写
双检加锁策略
多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。
后面的线程进来发现已经有缓存了,就直接走缓存。
import com.atguigu.redis.entities.User;
import com.atguigu.redis.mapper.UserMapper;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Service
@Slf4j
public class UserService {public static final String CACHE_KEY_USER = "user:";@Resourceprivate UserMapper userMapper;@Resourceprivate RedisTemplate redisTemplate;/*** 业务逻辑没有写错,对于小厂中厂(QPS《=1000)可以使用,但是大厂不行* @param id* @return*/public User findUserById(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysqluser = (User) redisTemplate.opsForValue().get(key);if(user == null){//2 redis里面无,继续查询mysqluser = userMapper.selectByPrimaryKey(id);if(user == null){//3.1 redis+mysql 都无数据//你具体细化,防止多次穿透,我们业务规定,记录下导致穿透的这个key回写redisreturn user;}else{//3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率redisTemplate.opsForValue().set(key,user);}}return user;}/*** 加强补充,避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况。* @param id* @return*/public User findUserById2(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql,// 第1次查询redis,加锁前user = (User) redisTemplate.opsForValue().get(key);if(user == null) {//2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (UserService.class){//第2次查询redis,加锁后user = (User) redisTemplate.opsForValue().get(key);//3 二次查redis还是null,可以去查mysql了(mysql默认有数据)if (user == null) {//4 查询mysql拿数据(mysql默认有数据)user = userMapper.selectByPrimaryKey(id);if (user == null) {return null;}else{redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);}}}}return user;}}
数据库和缓存一致性更新策略
给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。
我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以mysql的数据库写入库为准。
先更新数据库,再更新缓存
异常问题
1 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。
2 先更新mysql修改为99成功,然后更新redis。
3 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100 。
4 上述发生,会让数据库里面和缓存redis里面数据不一致,读到redis脏数据
异常问题
【先更新数据库,再更新缓存】,A、B两个线程发起调用【正常逻辑】1 A update mysql 1002 A update redis 1003 B update mysql 804 B update redis 80=============================
【异常逻辑】多线程环境下,A、B两个线程有快有慢,有前有后有并行1 A update mysql 1003 B update mysql 804 B update redis 802 A update redis 100=============================最终结果,mysql和redis数据不一致,o(╥﹏╥)o,mysql80,redis100
先更新缓存,再更新数据库
mysql一般作为底单数据库,保证最后解释
【先更新缓存,再更新数据库】,A、B两个线程发起调用【正常逻辑】1 A update redis 1002 A update mysql 1003 B update redis 804 B update mysql 80====================================
【异常逻辑】多线程环境下,A、B两个线程有快有慢有并行A update redis 100B update redis 80B update mysql 80A update mysql 100
----mysql100,redis80
先删除缓存,再更新数据库
异常问题
(1)请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql......A还么有彻底更新完mysql,还没commit(2)请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了)(3)请求B继续,去数据库查询得到了mysql中的旧值(A还没有更新完)(4)请求B将旧值写回redis缓存(5)请求A将新值写入mysql数据库 上述情况就会导致不一致的情形出现。
时间 | 线程A | 线程B | 出现的问题 |
---|---|---|---|
t1 | 请求A进行写操作,删除缓存成功后,工作正在mysql进行中… | ||
t2 | 1 缓存中读取不到,立刻读mysql,由于A还没有对mysql更新完,读到的是旧值 2 还把从mysql读取的旧值,写回了redis | 1 A还没有更新完mysql,导致B读到了旧值 2 线程B遵守回写机制,把旧值写回redis,导致其它请求读取的还是旧值,A白干了。 | |
t3 | A更新完mysql数据库的值 | redis是被B写回的旧值,mysql是被A更新的新值。出现了,数据不一致问题。 |
解决方案:延时双删策略
问题示例:
线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间。
这个时间怎么确定呢?
- 第一种方法:
在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,
以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。
这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。 - 第二种方法:
新启动一个后台监控程序,比如后面要讲解的WatchDog监控程序,会加时
这种方法吞吐降低怎么办?
使用异步
使用WatchDog
先更新数据库,再删除缓存
时间 | 线程A | 线程B | 出现的问题 |
---|---|---|---|
t1 | 更新数据库中的值… | ||
t2 | 缓存中立刻命中,此时B读取的是缓存旧值。 | A还没有来得及删除缓存的值,导致B缓存命中读到旧值。 | |
t3 | 更新缓存的数据 |
假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中,读取到的是缓存旧值。
微软云案例:https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside
阿里巴巴canal:
解决方案
- 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
- 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
- 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
- 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。
最终解决方案:最终一致性
案例:
- 流量充值,先下发短信实际充值可能滞后5分钟,可以接受
- 电商发货,短信下发但是物流明天见
总结
优先使用先更新数据库,再删除缓存的方案(先更库→后删存)。理由如下:
- 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql。
- 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂停并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性,这是理论可以达到的效果,但
实际,不推荐,因为真实生产环境中,分布式下很难做到实时一致性,一般都是最终一致性
如果使用先更新数据库,再删除缓存的方案
策略 | 高并发多线程条件下 | 问题 | 现象 | 解决方案 |
---|---|---|---|---|
先删除redis缓存,再更新mysql | 无 | 缓存删除成功但数据库更新失败 | Java程序从数据库中读到旧值 | 再次更新数据库,重试 |
有 | 缓存删除成功但数据库更新中…有并发读请求 | 并发请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值 | 延迟双删 | |
先更新mysql,再删除redis缓存 | 无 | 数据库更新成功,但缓存删除失败 | Java程序从redis中读到旧值 | 再次删除缓存,重试 |
有 | 数据库更新成功但缓存删除中…有并发读请求 | 并发请求从缓存读到旧值 | 等待redis删除完成,这段时间有数据不一致,短暂存在。 |
问题示例
- 你只要用缓存,就可能会涉及到redis缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
- 双写一致性,你先动缓存redis还是数据库mysql哪一个?why?
- 延时双删你做过吗?会有哪些问题?
- 有这么一种憶况,微服务查询redis无,mysql有,为保证数据双写一致性回写redis你需要注意什么?双减加锁策略了解过吗?如何尽量避免缓存击穿
- redis和mysql双写100%会出纰漏,做不到强一致性,你如何保证最终一致性?
相关文章:

Redis(十三)缓存双写一致性策略
文章目录 概述示例 缓存双写一致性缓存按照操作来分,细分2种读写缓存:同步直写策略读写缓存:异步缓写策略双检加锁策略 数据库和缓存一致性更新策略先更新数据库,再更新缓存先更新缓存,再更新数据库先删除缓存…...

7 scala的类构造器
在创建对象的时候,需要调用类的构造器。Scala 提供了主构造器和辅助构造器。 1 主构造器 与 Java 一样,如果我们没有特别定义,那么 Scala 提供的默认构造器是没有参数的。 我们可以在类名后,指定构造器的参数列表,列…...

如何在 Mac 上恢复永久删除的文件:有效方法
您是否错误地从 Mac 中删除了某个文件,并且确信它已经永远消失了?好吧,你可能错了。即使您认为已永久删除计算机上的数据,仍有可能将其恢复。 在本文中,您将了解如何在 Mac 上恢复永久删除的文件,并了解增…...

Web后端开发:事务与AOP
事务管理 在学习数据库时,讲到:事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求,要么同时成功,要么同时失败。 事务的操作主要有三…...

[word] word如何打印背景和图片? #微信#其他#经验分享
word如何打印背景和图片? 日常办公中会经常要打印文件的,其实在文档的打印中也是有很多技巧的,可以按照自己的需求设定,下面给大家分享word如何打印背景和图片,一起来看看吧! 1、打印背景和图片 在默认的…...

Maven - 编译报错:程序包 XXX 不存在(多模块项目)
问题描述 编译报错:程序包 XXX 不存在(多模块项目) 原因分析 检查依赖模块 pom 文件,看是不是引入了如下插件 <plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-pl…...

Vue事件中如何使用 event 对象
在Vue中,事件处理函数常常需要获取事件触发时的相关信息,比如鼠标位置、按键信息等。而要获取这些信息,就需要使用event对象。那么在Vue的事件中如何正确使用event对象呢?接下来就来详细介绍一下。 首先,在Vue的事件中…...

Golang GC 介绍
文章目录 0.前言1.发展史2.并发三色标记清除和混合写屏障2.1 三色标记2.2 并发标记问题2.3 屏障机制Dijkstra 插入写屏障Yuasa 删除写屏障混合写屏障 3.GC 过程4.GC 触发时机5.哪里记录了对象的三色状态?6.如何观察 GC?方式1:GODEBUGgctrace1…...

决策树之scikit-learn
实例 from sklearn.datasets import load_iris from sklearn import tree import matplotlib.pyplot as plt# Load iris dataset iris load_iris() X, y iris.data, iris.target# Fit the classifier clf tree.DecisionTreeClassifier() clf clf.fit(X, y)# Plot the deci…...

Python爬虫之关系型数据库存储#5
关系型数据库是基于关系模型的数据库,而关系模型是通过二维表来保存的,所以它的存储方式就是行列组成的表,每一列是一个字段,每一行是一条记录。表可以看作某个实体的集合,而实体之间存在联系,这就需要表与…...

ANSI Escape Sequence 下落的方块
ANSI Escape Sequence 下落的方块 1. ANSI Escape 的用途 无意中发现 B站有人讲解, 完全基于终端实现俄罗斯方块。 基本想法是借助于 ANSI Escape Sequence 实现方方块的绘制、 下落动态效果等。对于只了解 ansi escape sequence 用于 log 的颜色打印的人来说&…...

Vagrant 虚拟机工具基本操作指南
Vagrant 虚拟机工具基本操作指南 #虚拟机 # #vargant# #ubuntu# 虚拟机virtualbox ,VMWare及WSL等大家都很了解了,那Vagrant是什么东西? 它是一组命令行工具,可以象Docker管理容器一样管理虚拟机,这样快速创…...

中年低端中产程序员从西安出发到海南三亚低成本吃喝万里行:西安-南宁-湛江-雷州-徐闻-博鳌-陵水-三亚-重庆-西安
文章大纲 旅途规划来回行程的确定南宁 - 北海 - 湛江轮渡成为了最终最大的不确定性!感谢神州租车气温与游玩地点总体花费 游玩过程出发时间:Day1-1月25日星期四,西安飞南宁路途中:Day2-1月26日星期五,南宁-湛江-住雷州…...

企业级Spring boot项目 配置清单
目录 一、服务基础配置 二、配置数据库数据源 三、配置缓存 四、配置日志 五、配置统一异常处理 六、配置swagger文档 七、配置用户登录模块 八、配置websocket 九、配置定时任务 十、配置文件服务器 十一、配置Nacos 十二、配置项目启动数据库默认初始化(liquibas…...

WordPress函数wptexturize的介绍及用法示例,字符串替换为HTML实体
在查看WordPress你好多莉插件时发现代码中使用了wptexturize()函数用来随机输出一句歌词,下面boke112百科就跟大家一起来学习一下WordPress函数wptexturize的介绍及用法示例。 WordPress函数wptexturize介绍 wptexturize( string $text, bool $reset false ): st…...

【Iceberg学习三】Reporting和Partitioning原理
Metrics Reporting Type of Reports 从 1.1.0 版本开始,Iceberg 支持 MetricsReporter 和 MetricsReport API。这两个 API 允许表达不同的度量报告,并支持一种可插拔的方式来报告这些报告。 ScanReport(扫描报告) 扫描报告&am…...

肯尼斯·里科《C和指针》第12章 使用结构和指针(1)链表
只恨当时学的时候没有读到这本书,,,,,, 12.1 链表 有些读者可能还不熟悉链表,这里对它作一简单介绍。链表(linked list)就一些包含数据的独立数据结构(通常称为节点)的集…...

Xray 工具笔记
Xray 官方文档 扫描单个url(非爬虫) 并输出文件(不同文件类型) .\xray.exe webscan --url 10.0.0.6:8080 --text-output result.txt --json-output result.json --html-output report.html默认启动所以内置插件 ,指定…...

Linux环境下配置HTTP代理服务器教程
大家好,我是你们可爱的Linux小助手!今天,我将带你们一起探索如何在Linux环境下配置一个HTTP代理服务器。请注意,这不是一次火箭科学的实验,而是一次简单而有趣的冒险。 首先,我们需要明确什么是HTTP代理服…...

JavaEE作业-实验三
目录 1 实验内容 2 实验要求 3 思路 4 核心代码 5 实验结果 1 实验内容 简单的线上图书交易系统的web层 2 实验要求 ①采用SpringMVC框架,采用REST风格 ②要求具有如下功能:商品分类、订单、购物车、库存 ③独立完成,编写实验报告 …...

K8S容器挂了后重启状态正常,但应用无法访问排查处理
K8S容器挂了后重启状态正常,但应用无法访问排查处理 背景: 应用迁移K8S后因POD OOM挂了后重启,集群上POD状态正常,但应用无法访问。 排查: 查看应用日志,是启动时调用特权账号管理系统超时,…...

问题:老年人心理健康维护与促进的原则为________、________、发展原则。 #媒体#知识分享
问题:老年人心理健康维护与促进的原则为________、________、发展原则。 参考答案如图所示...

【超高效!保护隐私的新方法】针对图像到图像(l2l)生成模型遗忘学习:超高效且不需要重新训练就能从生成模型中移除特定数据
针对图像到图像生成模型遗忘学习:超高效且不需要重新训练就能从生成模型中移除特定数据 提出背景如何在不重训练模型的情况下从I2I生成模型中移除特定数据? 超高效的机器遗忘方法子问题1: 如何在图像到图像(I2I)生成模型中进行高效…...

Transformer的PyTorch实现之若干问题探讨(二)
在《Transformer的PyTorch实现之若干问题探讨(一)》中探讨了Transformer的训练整体流程,本文进一步探讨Transformer训练过程中teacher forcing的实现原理。 1.Transformer中decoder的流程 在论文《Attention is all you need》中࿰…...

解释Python中的GIL(全局解释器锁)及其影响。描述Python中的垃圾回收机制。Python中的类变量和实例变量有什么区别
解释Python中的GIL(全局解释器锁)及其影响 Python中的GIL(全局解释器锁)是CPython解释器中的一个机制,用于同步线程的执行。GIL确保任何时候只有一个线程在执行Python字节码。这意味着,即使在多核或多处理器…...

Appium使用初体验之参数配置,简单能够运行起来
一、服务器配置 Appium Server配置与Appium Server GUI(可视化客户端)中的配置对应,尤其是二者如果不在同一台机器上,那么就需要配置Appium Server GUI所在机器的IP(Appium Server GUI的HOST也需要配置本机IP…...

Java:JDK8新特性(Stream流)、File类、递归 --黑马笔记
一、JDK8新特性(Stream流) 接下来我们学习一个全新的知识,叫做Stream流(也叫Stream API)。它是从JDK8以后才有的一个新特性,是专业用于对集合或者数组进行便捷操作的。有多方便呢?我们用一个案…...

【Unity ShaderGraph】| 物体靠近时局部溶解,根据坐标控制溶解的位置【文末送书】
前言 【Unity ShaderGraph】| 物体靠近时局部溶解,根据坐标控制溶解的位置一、效果展示二、根据坐标控制溶解的位置,物体靠近局部溶解三、应用实例👑评论区抽奖送书 前言 本文将使用ShaderGraph制作一个根据坐标控制溶解的位置,物…...

测试OpenSIPS3.4.3的lua模块
这几天测试OpenSIPS3.4.3的lua模块,记录如下: 有bug,但能用 但现实世界就是这样,总是不完美的,发现之后马上提了issue 下面这段代码运行报错: function func1(msg) xlog("ERR","…...

【机器学习】数据清洗之处理缺失点
🎈个人主页:甜美的江 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:机器学习 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步…...