本文所说的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)
作用:
- 数据恢复:二进制日志记录了所有更改数据库结构或数据的操作(如
INSERT
、UPDATE
、DELETE
等),因此可以用于数据库恢复。在发生故障时,可以通过备份加上二进制日志来恢复到某个特定时间点的数据。 - 主从复制:二进制日志是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 Log 和 Redo 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 主要用于事务的持久性,确保已提交事务的数据在系统崩溃后不会丢失,保证事务的持久性。