【MySQL】MySQL 缓存方案

一、MySQL主从同步

1.1 主从同步是什么

  • MySQL 主从同步是一种数据复制机制,通过该机制可以实现将主数据库(Master)的 DDL(数据定义语言)和 DML(数据操纵语言,如 update、insert、delete)等操作同步到从数据库(Slave),从而保证主从数据库的数据一致性。

  • 这种机制在提高数据可用性、实现读写分离、进行数据备份与恢复等场景中发挥着重要作用。

1.2 主从同步原理

1.2.1 主从同步的组件

主从同步的实现依赖以下的组件:

  • binlog(二进制日志):主库产生的日志文件,记录了主库上所有的数据变更操作(如创建表、插入数据等),是主从同步的基础数据来源。

  • relay-log(中继日志):从库上的日志文件,用于存储从主库的 binlog 中复制过来的日志内容,相当于一个中间过渡的日志。

  • io-thread(IO 线程)

    • 主库的 IO 线程:负责接收从库 IO 线程的请求,并将 binlog 中指定位置后的日志内容发送给从库。
    • 从库的 IO 线程:负责连接主库,请求并接收主库发送的 binlog 日志,然后将其写入从库的 relay-log。
  • sql-thread(SQL 线程):从库上的线程,负责读取 relay-log 中的内容,并解析成具体的 SQL 语句在从库上执行,实现数据的重放(replay),从而保证从库与主库数据一致。

  • master-info 文件:从库上的文件,记录了已读取到的主库 binlog 的文件名和位置,方便从库下次请求主库时明确起始读取点。

1.2.2 主从同步的流程

主从同步的流程可以分为三部分:

  1. 主库生成binlog
  2. 从库获取binlog并写入relay-log
  3. 从库重放中继日志
主库生成binlog

主库执行 DML(如 update、insert、delete)或 DDL 操作后,会将这些操作记录到自身的 binlog 中,完成数据变更的日志化存储。

从库获取binlog并写入relay-log
  1. 从库的 IO 线程主动连接主库,并请求从指定 binlog 文件的指定位置(首次可从最开始)之后的日志内容。

  2. 主库接收到请求后,其负责复制的 IO 线程会根据请求的位置,读取对应 binlog 中的日志信息,然后返回给从库的 IO 线程。返回的信息除了日志内容,还包括当前主库 binlog 的文件名和位置。

  3. 从库的 IO 线程收到信息后,将日志内容追加到从库的 relay-log 末尾,同时将主库的 binlog 文件名和位置记录到 master-info 文件中,为下一次请求提供起始点。

从库重放中继日志

从库的 SQL 线程实时监测 relay-log,当发现有新增内容时,会解析这些内容,还原成主库上执行过的具体 SQL 语句,并在从库上执行这些语句,从而实现数据同步。
在这里插入图片描述

1.3 读写分离

MySQL 读写分离是基于主从同步机制实现的数据库架构优化方案,其核心思想是将数据库的写操作(如 INSERT、UPDATE、DELETE)集中在主库(Master)处理,而读操作(如 SELECT)分散到从库(Slave)执行,从而有效分担主库压力、提升系统整体性能。

在这里插入图片描述

1.3.1 读写分离的优点

  1. 减轻主库负载:主库仅处理写操作和必要的核心读操作,避免因大量读请求占用资源(如 CPU、IO)而影响写操作效率。

  2. 提高读操作吞吐量:通过多个从库分担读请求,利用分布式部署的优势提升系统整体的读性能,支持更多并发查询。

  3. 提升系统可用性:即使主库故障,从库仍可提供读服务,减少业务中断时间。

  4. 支持业务扩展:可根据读请求压力灵活增加从库数量,实现横向扩展,而无需修改核心业务逻辑。

1.3.2 读写分离的缺点

  1. 数据不一致:主从同步依赖 binlog 复制和 SQL 重放,从库数据通常滞后于主库,若写操作后立即从从库读取,可能获取旧数据,导致数据不一致

1.3.3 读写分离的适用场景

  • 读多写少业务:如电商商品列表、新闻资讯、社交平台动态等,读请求量远大于写请求。

  • 高并发查询场景:单库读性能无法满足并发需求(如秒杀活动中的商品库存查询),需通过从库分担压力。

  • 非核心写操作场景:写操作频率低(如后台数据录入),主库压力小,适合通过从库扩展读能力。

1.4 MySQL缓冲层

在 “读多写少、以 MySQL 为核心数据源、Redis 为缓存层” 的场景中,基于 MySQL 与 Redis 的缓存策略核是利用 Redis 的内存级读写速度提升读性能,同时通过合理的同步机制保证缓存与 MySQL 的数据一致性,避免因缓存引入脏数据或业务异常

1.4.1 为什么需要引入Redis

  • MySQL也有缓冲层,它的作用是用来缓存热点数据,这些数据包括索引、记录等,mysql 缓冲层是从自身出发,跟具体的业务无关,缓冲策略主要是 LRU,由于 mysql 的缓冲层(buffer pool)不由用户来控制,也就不能由用户来控制缓存具体数据

  • MySQL 数据主要存储在磁盘当中,适合大量重要数据的存储。磁盘当中的数据一般是远大于内存当中的数据,一般业务场景关系型数据库(mysql)作为主要数据库;

  • Redis是内存数据库,它的所有数据都存储在内存当中,当然也可以持久化到磁盘中,因此它的速度很快,很适合作为MySQL的缓存,存储与业务相关的热点数据,这些热点数据可以由用户自己定义

在这里插入图片描述

1.4.2 MySQL和Redis的同步问题

我们这里讨论的数据基于用户定义的热点数据,当引入 Redis 后,缓存与 MySQL 的数据可能存在 5 种状态:

  1. MySQL有,缓存无(正常)
  2. MySQL有,缓存有(正常)
  3. 二者都有,数据一致(正常)
  4. MySQL无,缓存有(不正常)
  5. 二者都有,数据不一致(不正常)
  • 需要明确的是,我们获取数据主要依据MySQL为主,如果缓存中不存在,那么我们只需要将MySQL中的数据同步到缓存即可,没有什么大问题

  • 如果缓存有,但是MySQL没有,这样就会产生脏数据

  • 如果MySQL和缓存都有这个数据,但是数据不一致,这样也会有问题

1.4.3 同步问题解决方案

解决方案1

读数据:

  • 先从缓存获取,如果有直接返回
  • 如果没有,从MySQL中获取
    • 如果MySQL有,同步到缓存并返回
    • 如果MySQL没有,返回空

作用

  • 通过 “缓存未命中时从 MySQL 加载并同步”,保证缓存数据最终与 MySQL 一致

写数据:

  • 先删除缓存,再写MySQL,后续数据同步使用中间件go-mysql-transfer等中间件处理

作用

  • 避免 “缓存与 MySQL 数据不一致”:删除缓存后,写 MySQL 期间缓存为空,其他服务读时会直接查 MySQL,拿到最新数据并同步到缓存
  • 保证 “写后立即读” 的正确性:服务 A 写完 MySQL 后立即读数据时,因缓存已被删除,读流程会查 MySQL 拿到最新数据

问题:

  • 中间件同步延迟:若中间件同步缓存的过程延迟,缓存会在短时间内为空,此时读请求全部走 MySQL,可能短暂增加 MySQL 压力
解决方案2

读数据:

  • 先从缓存获取,如果有直接返回
  • 如果没有,从MySQL中获取
    • 如果MySQL有,同步到缓存并返回
    • 如果MySQL没有,返回空

作用:读数据的流程和解决方案1完全一致

写数据:

  • 先写缓存,并设置过期时间(如200ms),再写MySQL
  • 后续数据同步使用中间件实现

作用

  • 减少缓存空窗期:写操作后 Redis 立即有新数据(而非像策略 1 那样先删除),短时间内读请求可直接从 Redis 获取,减轻 MySQL 压力。

  • 控制脏数据时长:即使 MySQL 写入失败,脏数据仅存在 200ms,过期后缓存失效,读请求会查 MySQL 拿到正确状态

问题

  • 短暂脏数据:若 MySQL 写入失败,Redis 中 200ms 内的新数据是 “假数据”,可能导致用户看到错误信息

在这里插入图片描述

1.4.3 其他缓存问题

在 MySQL 与 Redis 组成的缓存架构中,由于数据同步策略、并发请求及缓存特性等因素,可能出现缓存穿透、缓存击穿、缓存雪崩三种典型问题

1.4.3.1 缓存穿透

当请求查询的数据在 Redis 中不存在,且在 MySQL 中也不存在时,该请求会绕过 Redis 直接穿透到MySQL,若此类请求大量并发,会导致 MySQL 压力剧增,甚至崩溃

原因:请求的是 “不存在的数据”,缓存(Redis)和数据库(MySQL)均无记录,导致每次请求都直接访问
数据库

解决方案

  1. 缓存空值(<key, nil>)

    • 当 MySQL 中查询到数据不存在时,在 Redis 中缓存该 key 对应的空值(如 nil),并设置较短的过期时间(如几分钟)。
    • 下次相同请求会直接从 Redis 获取空值,避免穿透到 MySQL。
  2. 布隆过滤器

  • 预先将 MySQL 中所有存在的 key 存入布隆过滤器(一种高效的概率性数据结构)。
  • 请求到来时,先通过布隆过滤器判断 key 是否可能存在:
    • 若不存在,直接返回空结果,避免访问 Redis 和 MySQL;
    • 若可能存在,再依次查询 Redis 和 MySQL。
1.4.3.2 缓存击穿

当某个热点数据在 Redis 中过期(或不存在),但在 MySQL 中存在时,大量并发请求会同时穿透到 MySQL 读取该数据,导致 MySQL 瞬间压力骤增

原因:热点数据的缓存失效,引发并发请求集中访问数据库。

解决方案

  1. 分布式锁

    • 当 Redis 中查询不到数据时,先尝试获取分布式锁,只有获取锁的请求才能访问 MySQL。
    • 该请求从 MySQL 读取数据后,更新 Redis 缓存,再释放锁;其他未获取到锁的请求则休眠一段时间后重试,直到从 Redis 中获取到数据。
  2. 热点 key 永不过期

    • 对于访问频率极高的热点数据,在 Redis 中设置为永不过期,避免因过期导致的缓存失效。
1.4.3.3 缓存雪崩

在某一时间段内,Redis 中大量缓存 key 集中过期失效,或 Redis 服务宕机,导致大量请求无法从缓存获取数据,全部涌向 MySQL,造成 MySQL 压力过载,甚至整个系统服务崩溃

原因

  • 缓存集中失效(如大量 key 设置了相同的过期时间);
  • Redis 集群宕机(如节点故障、网络问题);
  • 系统重启导致 Redis 缓存数据丢失(未开启持久化或重启时间过长)。

解决方案

  1. 避免缓存集中失效

    • 过期时间随机化:为 key 设置过期时间时,在基础时间上增加随机值,避免大量 key 同时过期。
    • 分批更新缓存:对热点数据的缓存过期时间进行错峰设计,通过定时任务分批刷新,避免集中失效。
  2. Redis 高可用集群

    • 采用 Redis 哨兵模式(Sentinel)或集群模式(Cluster),确保单个节点宕机时,其他节点能自动接管服务,避免 Redis 整体不可用。
  3. 缓存持久化与预热

    • 开启持久化:Redis 开启 RDB 或 AOF 持久化,确保重启时能恢复缓存数据(包括过期信息)。
    • 热数据预热:若系统重启时间较长,重启前通过脚本将 MySQL 中的热点数据提前加载到 Redis,避免重启后缓存为空导致请求冲击数据库。

更多资料:https://github.com/0voice

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

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

相关文章

base64.b64encode(f.read()).decode(‘utf-8‘)作用

base64.b64encode(f.read()).decode(utf-8) 的作用是将文件内容&#xff08;通常是二进制文件&#xff09;编码为一个 UTF-8 格式的字符串。下面逐步解释这个过程&#xff1a;f.read()&#xff1a;读取文件 f 中的内容。这将返回文件的二进制数据。base64.b64encode()&#xff…

集合框架学习

目录 集合体系结构 Collection的常用方法 Collection的遍历方式 迭代器 增强for Lambda表达式 集合框架概述 集合体系结构 单列集合 Collection代表单列集合,每个元素(数据)只包含一个值 双列集合 Map代表双列集合,每个元素包含两个值(键值对) Collection集合特点 Li…

经典算法题解析:从思路到实现,掌握核心编程思维

算法是编程的灵魂&#xff0c;也是面试中的重点考察内容。本文精选了几道经典算法题&#xff0c;涵盖字符串处理、链表操作、树遍历等常见场景&#xff0c;通过详细解析帮助你理解算法设计思路与实现细节&#xff0c;提升解题能力。一、无重复字符的最长子串题目描述给定一个字…

【Unity游戏】——1.俄罗斯方块

搭建场景 使用任意方块、纯色瓦片或者其他图形作为背景&#xff0c;设置其大小与目标大小一致或者更大&#xff0c;设置左下角为场景顶点&#xff0c;并放置在&#xff08;0&#xff0c;0&#xff09;处。调整摄像机至合适位置。 制作游戏预制体 每个方块预制体包含有4个小方…

【C++进阶】---- 二叉搜索树

1.二叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结点的值都⼤于等于根结…

基于 OpenCV 与 sklearn 的数字识别:KNN 算法实践

在计算机视觉领域&#xff0c;数字识别是一个经典问题&#xff0c;广泛应用于邮政编码识别、车牌识别等场景。本文将介绍如何使用 OpenCV 进行图像处理&#xff0c;并结合 KNN&#xff08;K 近邻&#xff09;算法实现数字识别&#xff0c;同时对比 OpenCV 内置 KNN 与 scikit-l…

利用径向条形图探索华盛顿的徒步旅行

利用径向条形图探索华盛顿的徒步旅行 import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np import pandas as pdfrom matplotlib.cm import ScalarMappable from matplotlib.lines import Line2D from mpl_toolkits.axes_grid1.inset_locator impor…

火狐浏览器中国特供版关闭,如何下载 Firefox 国际版?如何备份数据?

火狐浏览器中国特供版关闭&#xff0c;如何下载 Firefox 国际版&#xff1f;如何备份数据&#xff1f;各位火狐老用户注意了&#xff01;7 月 27 日北京谋智火狐正式发布公告&#xff1a;2025 年 9 月 29 日 24:00 起&#xff0c;中国特供版账户服务将彻底关闭&#xff0c;所有…

C语言操作符详解:从基础到进阶

在C语言中&#xff0c;操作符是构建表达式的基础&#xff0c;掌握各类操作符的用法、优先级及特性&#xff0c;对写出高效且正确的代码至关重要。本文将系统梳理C语言操作符的核心知识点&#xff0c;包含实例代码与详细解析&#xff0c;助你彻底搞懂操作符。 1. 操作符的分类 C…

鸿蒙平台运行Lua脚本

1. 目标 使用 rust 在移动端实现 Lua 脚本的运行。 2. 核心步骤 [Rust Host App]│├── [mLua VM] (通过 mlua 或 rlua 库嵌入)│ ├── 独立Lua状态&#xff08;隔离执行&#xff09;│ ├── 受限标准库&#xff08;禁用危险函数&#xff09;│ └── 内存/CPU限…

【Ubuntu】发展历程

Ubuntu 是一个基于 Debian 的 Linux 发行版&#xff0c;由 Canonical 公司开发和维护。它以其易用性、稳定性和强大的社区支持而著称。以下是 Ubuntu 从发布以来的主要版本和发展历程&#xff1a;1. Ubuntu 4.10 "Warty Warthog" (2004)发布日期&#xff1a;2004年10…

k8s下springboot-admin 监控服务部署,客户端接入

踩坑及解决以下问题 1、客户端监控信息不显示,需要暴露监控检查接口路径 2、服务端不显示客户端日志,需要启用日志,并指定日志路径 3、解决在k8s下,客户端多实例注册id相同,如2个实例只显示一个 整体架构 springboot-admin 由服务端和客户端组成 服务端负责 1、提供 We…

git删除远程分支和本地分支

1. git删除远程分支 git push origin --delete [branch_name]2. 删除本地分支 2.1 git branch -d 会在删除前检查merge状态&#xff08;其与上游分支或者与head&#xff09;。 git branch -d [branch_name] 2.2 git branch -D 直接删除 git branch -D 是 git branch --delete…

Go 的时间包:理解单调时间与挂钟时间

Go 的时间包&#xff1a;理解单调时间与挂钟时间 &#x1f4c5; 引言 Go 语言自版本 1.9 起在 time.Time 中同时支持 “挂钟时间&#xff08;wall‑clock&#xff09;” 和 “单调时间&#xff08;monotonic clock&#xff09;”&#xff0c;用于分别满足时间戳与时间间隔测量…

Android启动时间优化大全

1 修改Android mksh默认的列长度 不修改这个参数&#xff0c;adb shell后&#xff0c;输入超过80个字符&#xff0c;就不能看到完整的命令行。external/mksh/src/sh.h EXTERN mksh_ari_t x_cols E_INIT(80); EXTERN mksh_ari_t x_lins E_INIT(24);2 Kernel优化 2.1 内核驱动模块…

matplotlib.pyplot: 底层原理简析与进阶技巧

文章目录 1 底层实现原理 1.1 核心架构 1.1 渲染流程 2 基础用法 2.1 基本绘图 2.2 多子图系统 2.3 高阶用法 2.3.1 自定义Artist对象 2.3.2 高级动画技术 2.3.3 事件处理系统 2.3.4 混合渲染技术 3 性能优化技巧 4 扩展模块 5 总结 5.1 底层原理关键点 5.2 进阶技巧 1 底层实现…

深入理解现代前端开发中的 <script type=“module“> 与构建工具实践

引言&#xff1a;模块化开发的演进在早期的前端开发中&#xff0c;JavaScript 缺乏原生的模块化支持&#xff0c;开发者不得不依赖 IIFE&#xff08;立即调用函数表达式&#xff09;或第三方库&#xff08;如 RequireJS&#xff09;来实现代码组织。随着 ES6&#xff08;ES2015…

yolo--qt可视化开发

qt5可能不支持我们的cuda版本&#xff0c;改用qt6 YOLO11QT6OpencvC训练加载模型全过程讲解_yolov11 模型转换成opencv c模型-CSDN博客 下面是qt5版本的案例&#xff0c;和yolo及cuda有冲突 安装qt 切换到虚拟环境&#xff0c;例如pyqt&#xff0c;conda activate pyqt pip …

SQL性能优化

show [session|global] status : 查看服务器状态 show global status like Com_ : 查看各种语句的执行次数 开启慢查询: 在 MySQL 配置文件&#xff08;/etc/my.cnf&#xff09;配置: #开启MySQL慢日志查询开关 slow_query_log1 #设置慢日志的时间为2秒&#xff0c;SQL语句执…

ctfshow pwn40

目录 1. 分析程序 2. 漏洞编写 3. 漏洞验证 1. 分析程序 首先检查程序相关保护&#xff0c;发现程序为32位且只开启了一个NX保护 checksec pwn 使用IDA进行逆向分析代码&#xff0c;查看漏洞触发点&#xff1a; 在main函数中&#xff0c;有一个ctfshow函数&#xff0c;这里…