使用缓存保护MySQL

news2025/1/13 15:50:32

1 更新缓存最佳实践

Redis的执行器非常薄,所以Redis只支持有限API,几乎没聚合查询能力,也不支持SQL。存储引擎也简单,直接在内存中用最简单数据结构保存数据。

如Redis的LIST在存储引擎的内存中的数据结构就是双向链表。内存是易失性存储,使用内存保存数据的Redis不保证数据可靠存储。Redis牺牲数据可靠性,换取高性能,适合做MySQL前置缓存。

虽Redis支持数据持久化,还支持主从复制,但仍是不可靠存储,天然不保证数据可靠性,所以做缓存,很少作为唯一的数据存储。

即使只是把Redis作为缓存来使用,也要考虑这“数据不可靠性”,程序使用Redis时,要兼容Redis丢数据情况,做到即使Redis丢数据,也不影响系统数据准确性。

缓存MySQL的一张表时,通常直接选用主键作为Redis中的Key,如缓存订单表,用订单表主键订单号作为Redis key。

如果Redis实例不是给订单表专用,还要给订单的Key加统一前缀,如“orders:888888”。Value用来保存序列化后的整条订单记录,可选择可读性较好的JSON序列化方式,也可选择性能更好且更节省内存的二进制序列化方式。

缓存中的数据要怎么更新。

查询订单数据时,先去缓存查询:

  • 命中缓存,直接返回订单数据
  • 没命中,去DB查询,得到查询结果后,把订单数据写入缓存,然后返回

更新订单数据时,先更新DB中的订单表,若更新成功,再更新缓存中的数据。

该缓存更新策略: Read/Write Through,绝大多数情况下可能都没问题。但并发下有概率出现“脏数据”,缓存中的数据可能被错误更新成旧数据。

如对同一条订单记录,同时产生一个读请求、一个写请求,被分配到两个不同线程并行执行:

  • 读线程R1尝试读缓存,没命中,去DB读到订单数据
  • 这时可能另一读线程R2抢先更新了缓存,在处理写请求线程W1中,先后更新DB、缓存。然后,拿订单旧数据的第一个读线程R1又把缓存更新成旧数据

这是一种case,还有如两线程对同一条订单数据并发写,也可能导致缓存“脏数据”,具体流程类似ABA。你不要觉得这种情况概率较小,出现“脏数据”概率和系统数据量及并发数量正相关,当系统数据量够大且并发够多,这种脏数据必现。

Cache Aside很好解决这问题,大多数情况是缓存最佳方式。Cache Aside模式和Read/Write Through很像,处理读请求逻辑一样,唯一差别:Cache Aside更新数据时,并非更新缓存,而是删除缓存。

订单服务收到更新数据请求后,先更新DB,若更新成功,再尝试删除缓存中订单:

  • 若缓存中存在这条订单就删除它
  • 若不存在就什么都不做

然后返回更新成功。这条更新后的订单数据将在下次被访问时,加载到缓存。Cache Aside更新缓存有效避免并发读写导致的脏数据问题。

2 缓存穿透导致的雪崩

缓存命中率低,就会出现大量“缓存穿透”

少量缓存穿透正常,需预防短时间内大量请求无法命中缓存,请求穿透到DB,导致DB忙,请求超时。大量请求超时会引发更多重试请求,更多重试请求让DB更忙,恶性循环导致雪崩。

系统初始化时,如系统升级重启或缓存刚上线,这时缓存空,若大量请求直接打过来,易引发大量缓存穿透,导致雪崩。为避免这种情况,可采用灰度发布,先接入少量请求,再逐步增加系统请求数量,直到全部请求都切换完成。

若不采用灰度发布,就在系统启动时对缓存预热:在系统初始化阶段,接收外部请求之前,先把最经常访问的数据填充到缓存,这样大量请求打过来,就不会出现大量缓存穿透。

缓存穿透时,若从DB读取数据时间较长,也易DB雪崩

如缓存数据是个复杂的DB联查结果,若在DB执行该查询需10s,那当缓存中这条数据过期后,最少10s内,缓存都不会有数据。

若这10s内有大量请求都要读取这缓存数据,这些请求都会穿透缓存,打到DB,易导致DB忙,当请求量较大,就会雪崩。

所以,若构建缓存数据需要的查询时间太长或并发量特别大,Cache Aside或Read/Write Through都可能出现大量缓存穿透。

对此无方法能应对所有场景,要针对业务场景选择合适解决方案。如牺牲缓存时效性和利用率,缓存所有数据,放弃Read Through策略所有的请求,只读缓存不读DB,用后台线程定时更新缓存数据。

3 总结

使用Redis作为MySQL的前置缓存,可以非常有效地提升系统的并发上限,降低请求响应时延。绝大多数情况下,使用Cache Aside模式来更新缓存都是最佳的选择,相比Read/Write Through模式更简单,还能大幅降低脏数据的可能性。

使用Redis的时候,还需要特别注意大量缓存穿透引起雪崩的问题,在系统初始化阶段,需要使用灰度发布或者其他方式来对缓存进行预热。如果说构建缓存数据需要的查询时间过长,或者并发量特别大,这两种情况下使用Cache Aside模式更新缓存,会出现大量缓存穿透,有可能会引发雪崩。

顺便说一句,我们今天这节课中讲到的这些缓存策略,都是非常经典的理论,早在互联网大规模应用之前,这些缓存策略就已经非常成熟了,在操作系统中,CPU Cache的缓存、磁盘文件的内存缓存,它们也都应用了我们今天讲到的这些策略。

所以无论技术发展的多快,计算机的很多基础的理论的知识都是相通的,你绞尽脑汁想出的解决工程问题的方法,很可能早都写在几十年前出版的书里。学习算法、数据结构、设计模式等等这些基础的知识,并不只是为了应付面试。

FAQ

分布式数据库Hive执行器和存储引擎,Hive不是个数据库,只是个执行器,存储引擎就是HDFS+Map-Reduce。在Hive中,一条SQL执行过程和MySQL差不多,Hive会解析SQL,生成并优化逻辑执行计划,然后它就会把逻辑执行计划交给Map-Reduce去执行了,后续生成并优化物理执行计划,在HDFS上执行查询这些事儿,都是Map-Reduce去干的。顺便说一下,Hive的执行引擎(物理执行引擎)可替换,所以就有Hive on Spark。

Cache Aside更新缓存会产生脏数据?

数据加版本号,写库时自动增一。更新缓存时,只允许高版本数据覆盖低版本数据。

Cache Aside应该是先删缓存后更新数据库吧?先更新数据库的话一旦缓存删除失败了,就会产生脏数据

严格来说,在并发情况下,二种方式都有可能产生脏数据。Cache Aside Pattern建议,先操作数据库,再操作缓存。

# Cache Aside

应用程序直接与DB、缓存交互,并负责对缓存的维护。

读数据时,先访问缓存,命中则直接返回。
如果不命中,则先查询DB,并将数据写到缓存,最后返回数据。

写数据时,同时更新DB和缓存。

# Read-Through

应用程序只与缓存交互,而对DB的读取由缓存来代理。

读数据时,先访问缓存,命中则直接返回。
如果不命中,则由缓存查询DB,并将数据写到缓存,最后返回数据。

# Write-Through

应用程序只与缓存交互,而对DB的写由缓存来代理。

写数据时,访问缓存,由缓存将数据写到DB,并将数据缓存起来。

例如使用Redis来缓存MySQL的数据,一般都是通过应用程序来直接与Redis、MySQL交互,我的理解是Cache Aside,包"是/否"删除Cache在内。

而Read-Through,像Guava LoadingCache,在load里面定义好访问DB的代码,后续的读操作都是直接与Cache交互了。

https://www.ehcache.org/documentation/3.8/caching-patterns.html
https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5178
https://dzone.com/articles/using-read-through-amp-write-through-in-distribute
https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside

1.为什么先更新mysql再更新(删除)redis比反过来好?

降低了脏数据出现的概率,前者产生脏数据是由于并发,后者几乎是必然,只要先写再读的请求发生,都会造成脏数据:先把redis中的缓存清了,然后读请求读不到去数据库中找到并更新在redis中。
2.为什么aside cache比read/write through好?
也是降低了脏数据出现的概率。前者只有读写先后访问数据库,又调转顺序访问redis时redis中出现脏数据,这个概率很小,而并发写时相当于不操作redis;而后者在并发写的情况下也容易脏。

用锁来解决并发问题。在读线程上写锁(说独占锁比较合适),是否跟MVCC相违背,MVCC不就是为了用来解决高并发带来的读写阻塞问题吗?我这边有两种解决思路不知可否:第一用版本控制,类似MVCC,第二种用Read/Write Through,写写并发在MVCC模式下依然是阻塞的,不算违背,所以只要把更新数据库与更新缓存放入统一事务中就行。读写并发不阻塞,是因为mysql用了快照读原因,那我们可以继续写线程更新缓存,读线程采用redis的setnx方式解决覆盖

mvcc可以很好的解决读写冲突,但是对于写写冲突,要么加锁,要么引入冲突检测机制,否则就会导致写倾斜的问题。这个在23中有详细的说明。

经常看到说用布隆过滤来解决缓存穿透问题,这个方案有实际的案例吗?
如果是真的可以那么怎么去操作呢?
先初始化所有可能存到缓存里面数据的key到一个足够大的布隆过滤器,然后如果有新增数据就就继续往过滤器中放,删除就从过滤器里面删(又看到说不用bit的话支持累加删除)
如果发现不在过滤器中就表示一定不存在,就无需查询了。如果在过滤器中也有可能不存在,这个时候在配合null值?

首先这是个经典的方案,靠谱是没问题的。它可以解决问题是,不用真正去查询数据集,就可以判断,请求的数据是不是,不在数据集内。如果不在就不用去查询数据集了。

不少数据库都内置了布隆过滤器来提升查询效率,比如HBase。

布隆过滤器的缺点就是有点复杂,实现难度还是挺大的。

如果缓存时有大量命中为null如何处理?如果命中null 也进行缓存,会导致缓存增长太快,容易被攻击
如果不缓存,又容易引起大量穿透?

没有完美解决方案。

首先,避免短时间大量人为的空值攻击,这个事儿应该在上层安全或者风控层面去解决。(即使无法判断是否空值攻击,至少要拦截住短时间大量的不正常访问请求)

剩余下来的就是业务上正常的查询返回空的情况,这种可能要从业务上来设计一下,尽量避免大量可能的空值查询。

以上2点做了之后,空值查询就会少多了,这个时候可以根据实际情况选择缓存空值,或者让空值穿透。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145594.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于ONNX人脸识别实例(SCRFD/ArcFace)-C#版

一、引用 Microsoft.ML.OnnxRuntime OpenCvSharp OpenCvSharp.Extensions二、人脸检测(Face Detection) using System; using System.Collections.Generic; using System.Linq; using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp;…

c++11 标准模板(STL)(std::deque)(十)

定义于头文件 <deque> std::deque 修改器 移除首元素 std::deque<T,Allocator>::pop_front void pop_front(); 移除容器首元素。若容器中无元素&#xff0c;则行为未定义。 指向被擦除元素的迭代器和引用被非法化。若元素是容器中的最后元素&#xff0c;则尾后…

2022 数字IC设计秋招复盘——数十家公司笔试题、面试实录

0 引言 秋招结束了。 “今年是前五年最差的一年&#xff0c;也将是后五年最好的一年”&#xff0c;虽然无法预知后面的就业情况&#xff0c;但就我今年自己的亲身经历与去年师兄师姐找工作的情况对比&#xff0c;感觉难度确实是增大了很多。我总共投递了80家左右的公司&#…

德云社相声春晚未播先火,郭德纲独揽三个节目,四位老艺术家助阵

随着央视春晚的二次彩排&#xff0c;德云社相声春晚&#xff0c;也被安排到议事日程当中&#xff0c;听说今年的相声春晚还颇有看点。由于缺少了岳云鹏张云雷等得力干将&#xff0c;郭德纲老师决定亲自下场&#xff0c;一个人就独揽了三个节目。 按说德云社举办相声春晚&#x…

乒乓普及套及廉价底板评测

疫情的末端期间开始打乒乓球&#xff0c;最开始在单位打&#xff0c;后来去了花园和大爷们打。用了几个拍子和胶皮&#xff0c;都是网上最便宜的&#xff0c;现在在野球场能排到前十吧&#xff0c;我打球比较“正”&#xff08;他人评价&#xff09;&#xff0c;大家比较愿意和…

基于Java+SpringBoot+vue+element等动物救助平台设计和实现

基于JavaSpringBootvueelement等动物救助平台设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取…

数字人民币创新浪潮来袭,支付机构如何“乘风破浪”?

易观&#xff1a;近年来&#xff0c;数字经济浪潮迭起&#xff0c;数字科技方兴未艾。法定数字货币作为各国政府掌握货币发行控制权的重要载体&#xff0c;正在全球范围内全面铺开。法定数字货币的推出将大幅提升货币的流转效率&#xff0c;为央行管理职能赋予了数字化内涵。可…

Golang 常用字符串函数

统计字符串长度&#xff0c;按字节 len(str)str : "你好" fmt.Println("len", len(str))字符串遍历&#xff0c;同时处理有中文的问题 s : []rune(str)str : "你好" s : []rune(str) for i : 0; i < len(s); i {fmt.Printf("string%c\n&…

安全知识答题小程序v2.0与v3.0的异同点一览

安全知识答题小程序安全知识答题小程序这个软件架构是微信原生小程序云开发。主要包含六大功能模块页面&#xff0c;首页、答题页、结果页、活动规则页、答题记录页、排行榜页。v2.0的功能有以下&#xff1a;排行榜页答题记录页活动规则页微信授权登录获取微信头像和昵称等首页…

SQL SELECT TOP, LIMIT, ROWNUM 子句

SQL SELECT TOP 子句 SELECT TOP 子句用于规定要返回的记录的数目。 SELECT TOP 子句对于拥有数千条记录的大型表来说&#xff0c;是非常有用的。 注意:并非所有的数据库系统都支持 SELECT TOP 语句。 MySQL 支持 LIMIT 语句来选取指定的条数数据&#xff0c; Oracle 可以使用…

java后端第五阶段:Git

一、开发场景 备份、代码还原、协同开发、追溯问题代码的编写人和编写时间&#xff01; 安装&#xff1a;直接去官网下载&#xff0c;傻瓜式安装 二、Git常用指令 1.设置用户签名 git config --global user.name 用户名 git config --global user.email 邮箱 2.初始化本地仓…

用存储过程、定时器、触发器来解决数据分析问题

做数据分析或者数据处理&#xff0c;我们也需要掌握这些技能&#xff0c;来解决特定的业务问题。比如&#xff1a;做自动化报表&#xff0c;如果数据需要每天实时更新&#xff08;增量爬虫&#xff09;、定时计算某个业务指标 、想要实时监控数据库表中的数据增、删、改情况等。…

四万字总结Redis语法、配置、实战

文章目录一、安装1.Linux下安装下载解压安装修改配置设置环境变量启动、连接查看redis进程退出2.windows下安装下载并解压二、系统管理1.常用key相关的命令2.时间相关命令3.设置相关命令CONFIG GET & CONFIG SET4.查询信息5.密码设置三、基本数据类型1.Redis strings2.Redi…

Golang常用结构源码01-Map

Golang集合源码-Map 22届211本科&#xff0c;在字节实习了一年多&#xff0c;正式工作了半年&#xff0c;工作主要是golang业务研发&#xff0c;go语言使用和生态比java简洁很多&#xff0c;也存在部分容易遇见的问题&#xff0c;常用结构需要对其底层实现略有了解才不容易写出…

小程序学习笔记

注册小程序账号 www.mp.weixin.qq.com 获取appid 微信开发者工具下载 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 设置代理和外观 创建小程序项目 小程序项目结构 app.json文件 这个文件的第一个路径就是小程序的默认页面 window配置项 …

使用kettle同步全量数据到Elasticsearch(es)--elasticsearch-bulk-insert-plugin应用

背景 为了前端更快地进行数据检索&#xff0c;需要将数据存储到es中是一个很不错的选择。由于公司etl主要工具是kettle&#xff0c;这里介绍如何基于kettle的elasticsearch-bulk-insert-plugin插件将数据导入es。在实施过程中会遇到一些坑&#xff0c;这里记录解决方案。 可能…

Java集合类ArrayList应用 | 二维数组的集合类表示与杨辉三角实现

目录 一、题干 &#x1f517;力扣&#xff1a;118. 杨辉三角 二、题解 1. 思路 2. 完整代码 三、总结 一、题干 &#x1f517;力扣&#xff1a;118. 杨辉三角 二、题解 1. 思路 我们知道杨辉三角的规律是&#xff1a; 每一行的第一列和它的最后一列上的数均为1.除此之…

如何在实验室服务器上跑代码

1.工具准备 可以下载一个xshell或secureCRT或者其他shell工具&#xff0c;通过ssh方式连接服务器&#xff0c;然后通过本地电脑终端控制服务器。连接方式输入主机&#xff08;Host&#xff09;,和端口号&#xff08;一般是22&#xff09;就行了。如下图 连接成功后就可以在本…

java面试题每日10问(1)

1.What is Java? Java is object-oriented, platform-independent, Multithreaded, and portable programming language.it provides its own JRE and API. 2.What is the difference between JDK, JRE, and JVM? JVM Java Virtual Machine provides the runtime environm…

返乡做县城跑腿平台困难吗?都有哪些需求要点需要掌握?

自2020年受疫情影响以来&#xff0c;大家对跑腿服务有了更全新的认识。跑腿的便利性和及时性让跑腿行业蓬勃发展。现如今全面开放之际&#xff0c;跑腿用户总数也将突破新高&#xff0c;其跑腿市场也将迎来快速发展期。 据统计&#xff0c;国内配送市场规模已超过400亿单&…