Redis的持久化机制

1,397 阅读10分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

1. 持久化机制

Redis的持久化就是把Redis中内存中的数据持久化到硬盘的过程。

client redis[内存] -----> 内存数据- 数据持久化-->磁盘

Redis官方提供了两种不同的持久化方法来将数据存储到硬盘里面分别是:

  • 快照(Snapshot)
    • 快照保存的是一个时刻数据状态
  • AOF (Append Only File) 只追加日志文件
    • AOF 只追加日志文件,将所有redis写命令记录到日志文件

1.1 快照(Snapshot)

1.1 特点

这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认开启持久化方式,因为保存的文件是以.rdb形式结尾的文件因此这种方式也称之为RDB方式。

在这里插入图片描述

Redis中的持久化机制默认使用的就是快照的方式,生成 dump.rdb 文件

在这里插入图片描述

1.2 快照生成方式

在Redis中快照生成的方式有两种

  • 客户端方式: BGSAVE 和 SAVE指令
  • 服务器配置自动触发,通过redis的配置达到配置条件之后redis服务器可以自动的触发快照

1.2.1 客户端指令方式

对于客户端方式来说,使用 BGSAVE 或者 SAVE 指令都可以生成快照文件,但是两种方式在执行方式上却有一些的区别。

  1. 客户端方式之BGSAVE
- 客户端可以使用BGSAVE命令来创建一个快照,当接收到客户端的BGSAVE命令时,redis会
- 调用fork来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令
- 请求,这种方式下快照的生成的同时不会阻塞Redis的服务,且官方也推荐使用这种方式。

  名词解释: fork是类unix系统中的一个函数用来创建子进程,当一个进程创建子进程的时
  候,底层的操作系统会创建该进程的一个副本,在类unix系统中创建子进程的操作会进行优
  化:在刚开始的时候,父子进程共享相同内存,直到父进程进行了写之后,对被写入的内存的
  共享才会结束服务。(假如总共有 2G 的内存,当父进程没有写命令时,因为父子进程共
  享内存,2G 内存全部给子进程,这样子进程生成快照更快,直到父进程接收到了写命
  令,子进程减小使用内存,把大量的内存给父进程,这种方式可以保证当父进程没有接收
  到客户端请求时,内存全部给子进程,可以更快地生成快照,且这种方式不会阻塞客户端
  同时还可以保证快照的创建)

在这里插入图片描述

在Redis客户端使用 BGSAVE 命令即可创建子进程生成快照

BGSAVE

在这里插入图片描述

  1. 客户端方式之SAVE
# 1.2.客户端方式之SAVE
- b.客户端还可以使用SAVE命令来创建一个快照,接收到SAVE命令的redis服务器在快照
创建完毕之前将不再响应任何其他的命令(客户端请求在redis创建快照完成之前将不再响
应任何其他的命令)

在这里插入图片描述

在这里插入图片描述

1.2.2 服务器配置自动触发方式

服务器配置方式之满足配置自动触发

  • 注意: SAVE命令并不常用,使用SAVE命令在快照创建完毕之前,redis处于阻塞状态,无法对外服务
- 如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后自动触发
- 一次BGSAVE命令,如果设置多个save配置选项,当任意一个save配置选项条件满足,redis
 也会触发一次BGSAVE命令

在这里插入图片描述

在这里插入图片描述

1.2.3 客户端向服务器发送关闭指令时

当客户端向服务器发送关闭指令时服务器也会做一次内存快照

# 服务器接收客户端shutdown指令
- 当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的
客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器

在这里插入图片描述

1.3 配置生成快照名称和位置

# 1.修改生成快照名称
- dbfilename dump.rdb

# 2.修改生成位置
- dir ./			(默认的是 ./ 代表当前目录,当前目录是基于 redis-server 脚本的当前目录)

我们可以修改 redis-conf配置文件去配置生成快照的名称和位置

在这里插入图片描述

在这里插入图片描述

对于快照持久化的方式来说,如果新添加了数据而此时还没有达到快照生成的条件时突然宕机,就会造成数据丢失问题。

1.2 AOF 只追加日志文件

2.1 特点

这种方式可以将所有客户端执行的写命令记录到日志文件中,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行一次AOF文件所包含的所有写命令,就可以恢复AOF文件的记录的数据集。AOF 方式是自动追加的,只要开启了AOF持久化,所有的写操作都会被以命令的方式记录到AOF文件中

在这里插入图片描述

2.2 开启AOF持久化

在redis的默认配置中AOF持久化机制是没有开启的,需要修改配置文件在配置中开启

# 1.开启AOF持久化
- a.修改 appendonly yes 开启持久化
- b.修改 appendfilename "appendonly.aof" 指定生成文件名称
- 注意: 默认这个文件的放置位置是由快照的.dir决定的 .dir默认配置的位置是 redis-server 
脚本的当前目录

在这里插入图片描述

在这里插入图片描述

2.3 日志追加频率

# 1.always 【谨慎使用】
- 说明: 每个redis写命令都要同步写入硬盘,严重降低redis速度
- 解释: 如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制;
- 注意: 转盘式硬盘在这种频率下200左右个命令/s ; 固态硬盘(SSD) 几百万个命令/s;
- 警告: 使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的写入放大问题,导致将固态硬盘的寿命从原来的几年降低为几个月。

# 2.everysec 【推荐】
- 说明: 每秒执行一次同步显式的将多个写命令同步到磁盘
- 解释: 为了兼顾数据安全和写入性能,用户可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis可以保证,即使系统崩溃,用户最多丢失一秒之内产生的数据。

# 3.no	【不推荐】
- 说明: 由操作系统决定何时同步 
- 解释:最后使用no选项,将完全由操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响但是系统崩溃时,会丢失不定数量的数据,另外如果用户硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘数据填满时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢。

2.4 修改同步频率

# 修改日志同步频率
- 修改appendfsync everysec|always|no 指定

在这里插入图片描述

如果快照和AOF两种持久化机制都开启,Redis会使用AOF持久化机制,因为AOF持久化存储的数据比快照存储的数据相对来说更准确。

1.3 AOF文件的重写

3.1 AOF带来的问题

AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了AOF重写(ReWriter)机制。

3.2 AOF重写

用来在一定程度上减小AOF文件的体积

3.3 触发重写方式

触发重写的方式有两种

  • 客户端方式触发重写
  • 服务器配置方式自动触发
  1. 客户端方式触发重写

执行BGREWRITEAOF命令 (不会阻塞redis的服务)

BGREWRITEAOF

重写前

在这里插入图片描述

客户端执行 BGREWRITEAOF 命令

在这里插入图片描述

重写后

在这里插入图片描述

  1. 服务器配置方式自动触发
  • 配置redis.conf中的auto-aof-rewrite-percentage选项 参加下图↓↓↓
  • 如果设置auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%)时,会自动触发,如果重写过于频繁,用户可以考虑将auto-aof-rewrite-percentage设置为更大

在这里插入图片描述

3. 4 重写原理

注意:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,替换原有的文件这点和快照有点类似。

# 重写流程
- 1. redis调用fork ,现在有父子两个进程 子进程根据内存中的数据生成此时刻的内存快照,然后往临时文件中写入数据(以命令的形式写入,重建数据的命令)
- 2. 父进程继续处理客户端请求,除了把重写之前的写命令写入到原来的aof文件中。同时把重写时收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 3. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

在这里插入图片描述

3.4 持久化总结

两种持久化方案既可以同时使用(aof),又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定。

无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方)。