中间件专栏之MySQL篇——MySQL缓存策略

本文所说的MySQL缓存策略与前文提到的buffer pool不同,那是MySQL内部自己实现的,本问所讲的缓存策略是使用另一个中间件redis来缓存MySQL中的热点数据。

一、为什么需要MySQL缓存方案

缓存用户定义的热点数据,用户可以直接从缓存中获取热点数据,降低数据库的读写压力。具体的原因如下:1、内存访问速度约是磁盘的10万倍,可极大节省时间;2、一般使用缓存的场景读的需求远远大于写的需求;3、MySQL自身的缓冲层与业务无关,无法实现用户自定义热点数据的缓存;4、一般而言,MySQL作为主数据库,redis作为辅助数据库,存放热点数据(主数据库一定是磁盘数据库,因为要保证数据的安全)。

二、缓存方案的基本原理

redis和MySQL数据一致性状态分析,共分为5种情况:

1、数据MySQL有,redis无;

2、数据MySQL无,redis有;

3、数据MySQL有,redis有,但不一致;

4、数据MySQL有,redis有,且一致;

5、数据MySQL无,redis无;

其中,状态1,4,5是合理的状态,2,3是不合理状态,并且状态4是理想状态,其他状态需要向状态4靠拢。

用户定义的热点数据如何读写?

读:先读redis(下文统称缓存),缓存存在则直接返回;不存在则访问MySQL,再写入缓存。

写:1、保证安全的方法:先删除缓存中的数据,再写入MySQL,最后同步至缓存;

2、提高效率但损失部分安全性的方法:先写入缓存并设置过期时间(过期时间=MySQL网络传输时间+MySQL处理时间+MySQL同步到缓存时间),再写入MySQL,最后MySQL再同步至缓存。该方法的唯一脆弱性在于MySQL还没同步到缓存的那段时间,可能出现数据不一致。(描述的比较抽象,读者若不理解可在评论区留言)。

上述讲到的MySQL同步至缓存,如何同步?

常用的有go-mysql-transfer,主要原理是伪装MySQL从数据库,此处不赘述,可自行查阅资料。

三、缓存方案的常见故障问题及解决方案

在使用Redis缓存用户定义的热点数据时,可能会遇到一些故障问题,通常会影响到应用的性能和数据一致性。以下是一些常见的故障问题及其解决方案:

1. 缓存雪崩

问题描述:缓存雪崩是指在同一时间大量缓存失效或重启缓存时,所有的请求都直接访问数据库,造成数据库压力骤增,进而引发系统崩溃或性能下降。

解决方案

  • 缓存失效时间错峰:为不同的缓存设置不同的过期时间,避免所有缓存同时失效。例如,可以给热点数据设置较长的过期时间,并随机化失效时间。
  • 使用Redis持久化机制:启用Redis的AOF(Append Only File)或RDB(快照)持久化机制,在Redis重启时可以恢复缓存数据,避免每次重启都从数据库加载。
  • 引入多级缓存:在Redis之外引入本地缓存(如应用服务器本地内存)来缓解Redis压力。

2. 缓存击穿

问题描述:缓存击穿是指某个热点数据的缓存失效时,多个请求同时访问数据库,造成数据库压力过大。特别是当某些热点数据缓存已经过期时,所有请求都直接访问数据库,可能会导致系统性能问题。

解决方案

  • 加锁机制 :当缓存失效时,可以使用分布式锁(如Redis的SETNX)来保证同一时刻只有一个请求去加载数据,其他请求等待缓存更新,减少对数据库的压力。
  • 使用队列缓存更新机制:可以使用队列(如Redis的List或Stream)来将数据加载的请求排队,避免多个请求同时去访问数据库。

3. 缓存数据不一致

问题描述:由于缓存和数据库的数据不同步,可能会导致数据不一致的问题。例如,数据库中的数据发生了变更,而缓存没有及时更新。

解决方案

  • 缓存更新策略
    • 主动更新:当数据库中的数据发生变化时,主动更新或删除相应的缓存。这通常通过数据库触发器或应用层代码来实现。
    • 延迟双删策略:在缓存更新时,先删除缓存,再写入新的缓存。如果出现缓存未更新的情况,再进行第二次删除缓存,避免由于并发问题导致缓存一致性问题。
  • 使用定时任务同步:定时任务可以用于同步数据库和缓存的数据,定期清除过期或失效的缓存数据。

4. Redis单点故障

问题描述:如果Redis作为缓存的唯一节点,发生故障(如宕机),将会导致整个缓存不可用,影响应用的性能。

解决方案

  • Redis集群或哨兵模式:使用Redis的高可用方案(如Redis Sentinel、Redis Cluster等)来保证Redis的高可用性。这样即使主节点宕机,Redis集群或哨兵可以自动进行主从切换,保证服务的连续性。
  • 持久化机制:启用Redis的AOF或RDB持久化机制,确保数据在Redis崩溃后能够恢复。

5. 缓存穿透

问题描述:缓存穿透是指请求访问的数据既不存在于缓存中,也不存在于数据库中,导致每次请求都直接查询数据库,增加数据库负载。

解决方案

  • 使用布隆过滤器:在缓存前面加一个布隆过滤器,当某个数据不存在时,布隆过滤器可以直接判断并阻止请求访问数据库。
  • 空值缓存:对于不存在的数据,可以将空值缓存一段时间,避免同样的请求频繁访问数据库。比如,当查询的数据不存在时,可以将返回值为空的情况缓存,设置较短的过期时间。

6. 缓存容量限制

问题描述:Redis缓存有限,当缓存数据量过大时,可能会出现Redis内存不足,导致缓存数据丢失或者缓存失效。

解决方案

  • LRU(Least Recently Used)淘汰策略:Redis提供了多种淘汰策略,默认使用LRU算法,在内存不足时,Redis会自动删除最久未使用的缓存数据。可以根据业务需求调整淘汰策略。
  • 合理设计缓存策略:对缓存数据进行合理设计,定期清理过期缓存,避免无意义的数据存储占用内存。

其中1,2,5是最为常见的故障。

四、MySQL三大日志的作用、区别以及刷盘时机

1. 二进制日志(Binlog)

作用:
  • 数据恢复 :二进制日志记录了所有更改数据库结构或数据的操作(如INSERTUPDATEDELETE等),因此可以用于数据库恢复。在发生故障时,可以通过备份加上二进制日志来恢复到某个特定时间点的数据。
  • 主从复制:二进制日志是MySQL主从复制的基础。主数据库将操作写入二进制日志,从数据库通过读取主数据库的二进制日志来同步数据。
  • 审计:可以用来记录数据库的所有更改操作,帮助审计和追踪某些特定操作的执行情况。
刷盘时机:
  • Binlog的写入是按事务提交的,即当事务提交时,binlog会同步到磁盘。使用sync_binlog参数控制刷新频率。
  • sync_binlog=0:表示不强制写入,可能会丢失binlog日志。
  • sync_binlog=1:每次写入都会强制刷新binlog到磁盘。
  • sync_binlog>1:表示每次写入时会在多个binlog文件之间做同步,降低写入频率,但增加一些风险。

2. 错误日志(Error Log)

作用:
  • 记录启动和停止:记录MySQL服务器的启动和关闭过程中的各种信息,包括启动、停止、重启等日志。
  • 错误记录:记录运行中的各种错误信息,如语法错误、权限问题、资源不足等。
  • 警告信息:记录数据库在运行过程中遇到的一些警告信息或非致命错误。
刷盘时机:
  • 错误日志通常在数据库启动时和操作过程中进行记录,通常以文件的形式存在并实时更新。因此,错误日志的刷盘时机通常为每次写入日志时即进行刷新,不需要额外的配置。

3. 查询日志(General Log)

作用:
  • 记录所有查询:查询日志会记录MySQL服务器接收到的所有SQL查询语句(无论是否成功执行),这对于开发、调试、性能分析非常有用。
  • 性能分析:通过查看查询日志,可以分析哪些查询语句执行时间较长,或者执行频率较高,从而帮助优化SQL查询。
区别:
  • 查询日志与错误日志
    • 查询日志记录的是所有执行的SQL语句,不管是否出错;
    • 错误日志记录的是MySQL启动/停止/崩溃等相关的错误信息,以及执行失败的SQL语句。
  • 查询日志与二进制日志
    • 查询日志记录所有的查询操作,但不涉及数据的改变,它是"所有SQL语句的镜像";
    • 二进制日志记录的是实际对数据库数据产生变化的操作,通常会比查询日志更为简洁且有用,用于恢复和主从同步。
刷盘时机:
  • 查询日志会随着每次SQL语句的执行而实时记录,所以一般会进行实时刷盘。

刷盘时机总结:

  • 二进制日志(Binlog) :根据sync_binlog参数设置,常见的做法是每次事务提交时写入磁盘。
  • 错误日志(Error Log):实时刷盘,记录数据库运行状态和错误信息。
  • 查询日志(General Log):实时刷盘,记录所有的查询操作。

总结:

日志类型 主要功能 是否包含数据变化 是否记录所有操作 刷盘时机
二进制日志 数据恢复、主从复制、审计 事务提交时写入
错误日志 记录数据库错误、启动/停止等信息 实时刷盘
查询日志 记录所有SQL查询 实时刷盘

PS:还有一种说法是三大日志是binlog,redolog,undolog,因此我把另两种也总结在下面。

Undo LogRedo Log 是 MySQL 中事务日志的一部分,用于支持事务的回滚(Undo)和恢复(Redo) 。

1. Undo Log

作用:
  • 事务回滚:Undo Log 主要用于支持事务的回滚(Rollback)操作。当一个事务中的操作出现错误或用户主动回滚时,Undo Log 用于撤销事务中已经进行的操作,将数据恢复到事务开始之前的状态。
  • 多版本并发控制(MVCC) :Undo Log 也被 MySQL 用于实现 多版本并发控制,即不同事务可以并发操作同一行数据而不冲突。通过存储数据的历史版本,其他事务可以读取旧的版本,避免锁冲突。
  • 保持一致性:在数据库崩溃或重启时,Undo Log 会确保未提交的事务所做的修改不会被永久保留,从而保证数据库的一致性。
刷盘时机:
  • Undo Log 的刷盘通常是与 事务的提交和回滚 相关的。当事务提交或回滚时,Undo Log 记录会写入磁盘。如果事务未提交,则这些记录会被删除。
  • 一般情况下,Undo Log 是缓存在内存中的,在事务提交时会刷新到磁盘,具体的刷盘频率和配置取决于 innodb_flush_log_at_trx_commit 配置。

2. Redo Log

作用:
  • 事务恢复:Redo Log 记录了已提交事务的操作,用于数据库崩溃后的恢复操作。Redo Log 中包含了已提交事务对数据库数据的修改,数据库崩溃时,通过 Redo Log 来确保所有已提交的事务不会丢失。
  • 确保持久性(Durability):Redo Log 是 MySQL InnoDB 存储引擎实现事务持久性(Durability)特性的关键。即使数据库发生崩溃,只要事务已经提交,相应的数据修改也会被持久化。
  • 崩溃恢复:在 MySQL 崩溃恢复时,Redo Log 会被用来恢复已提交的事务。这是通过重放 Redo Log 中记录的操作来实现的。
刷盘时机:
  • Redo Log 的写入频率可以通过 innodb_flush_log_at_trx_commit 来控制。常见的值有:
    • innodb_flush_log_at_trx_commit = 1:每次事务提交时,都会将 Redo Log 刷新到磁盘,确保事务持久性。这种设置保证了最强的数据一致性,但性能开销较大。
    • innodb_flush_log_at_trx_commit = 2:每次事务提交时,Redo Log 写入到操作系统的缓冲区,但不立即刷新到磁盘。系统崩溃时,可能会丢失一些未刷新的日志,但性能相对较好。
    • innodb_flush_log_at_trx_commit = 0:Redo Log 写入到内存,但并不立刻刷新到磁盘。性能最好,但崩溃恢复时可能会丢失已提交事务的数据。

Undo Log 和 Redo Log 的区别

特性 Undo Log Redo Log
功能 支持事务回滚,撤销操作,保证一致性 支持事务恢复,保证持久性
内容 记录事务对数据的修改前的值(用于回滚) 记录事务对数据的修改后的值(用于恢复)
是否持久化 不持久化,仅在事务中有效 持久化,确保崩溃恢复时不会丢失已提交的事务数据
使用场景 事务回滚,MVCC(多版本并发控制) 事务提交后,数据库崩溃恢复时
刷盘时机 事务提交或回滚时写入磁盘 事务提交时,具体配置决定刷新频率
存储位置 存储在内存中,在事务提交或回滚时写入磁盘 存储在磁盘上,一直保留用于崩溃恢复

总结:

  • Undo Log 主要用于支持事务回滚和多版本并发控制,确保事务操作可以撤销,并且保持数据一致性。
  • Redo Log 主要用于事务的持久性,确保已提交事务的数据在系统崩溃后不会丢失,保证事务的持久性。
相关推荐
航叔啦1 小时前
Mysql和sqlServer命令比较
数据库·mysql·sqlserver
中草药z4 小时前
【RabbitMQ】RabbitMQ的核心概念与七大工作模式
分布式·学习·中间件·消息队列·rabbitmq·queue·工作模式
stars4 小时前
mysql表分区
数据库·mysql
m0_748235075 小时前
【MySQL】数据库开发技术:内外连接与表的索引穿透深度解析
android·mysql·数据库开发
GzlAndy5 小时前
MySQL执行更新SQL流程
数据库·sql·mysql
大霞上仙5 小时前
安装mysql
数据库·mysql
serendipity_hky5 小时前
【easy视频 | day04】视频转码 + 稿件管理 + 服务端审核稿件
java·数据库·redis·缓存·音视频
文弱书生子6 小时前
中间件专栏之MySQL篇——MySQL的基本原理和基本操作
数据库·mysql·中间件
数据知道7 小时前
Redis 指令备忘清单(超级详细!)
数据库·redis·缓存
Eugene__Chen7 小时前
MySQL面试01
数据库·mysql