五、Redis 持久化

一、Redis 持久化

Redis 提供了 RDB 和 AOF 两种不同级别的持久化方式:

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.

  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。

  • 可以同时开启两种持久化方式,在这种情况下,当redis重启的时候会 优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

一、RDB

RDB方式持久化是通过快照(snapshotting)完成的,当符合一定条件时自动将内存中的所有数据生成一份副本并存在硬盘上。

RDB 执行时间点是根据配置规则进行自动快照,RDB先将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件。

1. 优点:

  • RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份。
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心,适用于灾难恢复。
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

2. 缺点:

  • RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。
  • RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是可以调节重写日志文件的频率来提高数据集的耐久度。

3. 规则配置

配置规则由两个参数:时间窗口M和改动的键的个数N,每当时间M内被更改的键的个数大于N时,即符合自动快照条件,每条快照条件占一行并且以save参数开头,例:

# save <seconds> <changes>:  配置将DB 数据保存到硬盘的策略。
# 900秒内至少有一个 keys 改动,就会触发持久到硬盘
save 900 1
# 300秒内至少有10个 keys 改动,就会触发持久到硬盘
save 300 10
# 60秒内至少有10000个 keys 改动,就会触发持久到硬盘
save 60 10000

# 当Redis开启了RDB 并且在最后一次做写RDB失败时,会停止接收写入请求,导至Redis 不可用。
# 以提醒用户数据没有正确的持久化到硬盘。
# 如果RDB进程恢复正常工作后,将会自动恢复接上写入请求。
# 
# 如果已经有设置对Redis 服务和持久化进行监控,且在持久化出错时,不想停止写入服务。可以禁用该配置。
stop-writes-on-bgsave-error yes

# 默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。
# 如果不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
rdbcompression yes

# 在 Redis 5 开始会在 RDB 文件末尾生成一个 CRC64 校验值。启用该校验需要额外消耗10%的性能。
# 如果想要更好的性能,可以禁用该设置。
rdbchecksum yes

# 设置RDB 快照的文件名,默认是 dump.rdb
dbfilename dump.rdb

# 设置持久化文件的存放目录,默认是和当前配置文件保存在同一目录。
dir /var/redis/6379

4. RDB 快照过程:

  1. Redis 使用 fork 函数复制一份当前进程(父进程)的副本(子进程)

  2. 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件

  3. 子进程先将数据写入一临时文件,写完所有数据后会用该临时文件替上次持久化的RDB文件

  4. 在执行 fork 的时候操作系统会使用写时复制(copy-on-write)策略,即 fork 函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时,系统会将该片数据复制一份以保证子进程的数据不受影响,所以新的RDB文件存储的是执行 fork 一刻的内存数据。

    写时复制策略也保证了 fork 时刻虽然看上去生成两份内存副本,但实际内存的占用量不会增加一倍。

    但当快照过程中,如果写入操作过多,造成 fork 前后数据差异较大,会使内存使用量显著超过实际数据大小。

5. RDB 相关命令

  1. 当执行 SAVE 命令时,Redis同步地进行快照操作,在快照执行的过程中会阻塞所有来自客户端的请求。当数据比较多时,这一过程会导致Redis较长时间不能响应

  2. BGSAVE 命令可以在后台异步地进行快照操作,快照的同时服务器还能继续响应客户端的请求。

  3. LASTSAVE 命令获取最近一次成功快照的时间,返回Unix时间戳

  4. FLUSHALL 会清除数据库中的所有数据,不论清空数据库的过程是否触发了自动快照条件,只要自动快照条件不为空,Redis就会执行一次快照操作。如果没有定义自动快照条件,执行FLUSHALL则不会进行快照

  5. 执行 复制(replication) 时,当设置了主从模式时,Redis会在复制初使化时进行快照。

  6. Radis默认会将快照文件存储在Redis当前进程的工作目录中的dump.rdb文件中,可以通过配置dir和dbfilename两个参数分别指定快照文件存储的路径和文件名。

  7. Redis 启动 后会读取RDB快照文件,将数据从硬盘载入到内存,通常一个记录1000万个字符串类型键,大小为1G的快照文件载入到内存需要20-30秒。

二、AOF (append only file)

当使用Redis存储非临时数据时,一般需要打开AOF持久化来降低进程中止导致的数据丢失,AOF将Redis执行的每一条写命令追加到硬盘文件中。

1. 优点:

  • 使用AOF 会让你的Redis数据更完整:可以使用不同的 fsync 策略:无 fsync,每秒 fsync,每次写的时候 fsync。使用默认的每秒 fsync 策略 (fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
  • AOF文件是一个只进行追加的日志文件,所以不需要写入 ***磁盘寻道 (seek)***,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,也可使用redis-check-aof工具修复这些问题。
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行 重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议 的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

2. 缺点:

  • 对于相同的数据集来说,AOF 文件的体积 通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度 可能会慢于 RDB 。

3. AOF 配置:

# 默认是没有开启AOF(append only file)方式持久化,可以通过appendonly参数启用 
appendonly true 

# 目录和RDB文件目录一样,默认文件名是appendonly.aof,可通过append filename参数修改: 
appendfilename "appendonly.aof"

#由于操作系统的缓存机制,数据并没有立即写入硬盘,而是进入了硬盘的缓存,默认系统每30秒会执行一次同步操作,以将缓存中的数据真正地写入硬盘,在这30的过程中如果异常退出则会导制硬盘缓存中的数据丢失,设置同步的时机: 
# appendfsync always #每次修改同步一次
appendfsync everysec #每秒同步一次
# appendfsync no #no表示交同操作系统来做即30秒一次。 

# 设置AOF文件的重写 
# BGREWRITEAOF命令手动执行AOF重写 
# 当目前的AOF文件大小超过上一次重写时的大少的百分之多少时会进行重写,如果之前没有重写,则以启动时的大小为依据 
auto-aof-rewrite-percentage 100 

# 限制允许重写的最小AOF文件大小 
auto-oaf-rewrite-min-size 60mb 

# aof-load-truncated的默认值为 yes。当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。
# 如果是no,用户必须手动redis-check-aof修复AOF文件才可以重启Redis服务器。
aof-load-truncated yes
 
# 对于 persistence 持久化存储,Redis 有两种持久化方案,RDB(Redis DataBase)和 AOF(Append-Only File)。其中RDB是一份内存快照,AOF则为可回放的命令日志,他们各有特点也相互独立。4.0开始允许使用RDB-AOF混合持久化的方式,结合了两者的优点,通过 aof-use-rdb-preamble 配置项可以打开混合开关。
#
# 表示是否开启混合存储,默认是开启的。
# 开启后Redis保证RDB转储跟AOF重写不会同时进行。
# 当Redis启动时,即便RDB和AOF持久化同时启用且AOF,RDB文件都存在,则Redis总是会先加载AOF文件,这是因为AOF文件被认为能够更好的保证数据一致性。
aof-use-rdb-preamble yes

4. AOF 重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。如对同一个 Key 进行了一万次的 SET,那么AOF 会保存一万次的记录 ,实际上只有最后一条是有效的。

Redis 可以在不打断服务客户端的情况下,对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件,这个文件包含重建当前数据集所需的最少命令。

AOF 重写过程
  1. Redis 执行 fork() ,现在同时拥有父进程和子进程。

  2. 子进程开始将新 AOF 文件的内容写入到临时文件。

  3. 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。

  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。

  5. 现在 Redis 使用 rename(2) 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

5. BGREWRITEAOF 手动执行AOF重写

Redis BGREWRITEAOF 命令用于异步执行一个 AOF(AppendOnly File)文件重写操作。AOF 重写由 Redis 自行触发, BGREWRITEAOF仅仅用于手动触发重写操作。

即使 BGREWRITEAOF 执行失败,也不会有任何数据丢失,因为旧的AOF文件在BGREWRITEAOF 成功之前不会被修改。

6. AOF文件损坏修复

服务器可能在程序正在对 AOF 文件进行写入时停机,造成了 AOF 文件出错, 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

  • 为现有的 AOF 文件创建一个备份。
  • 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:
  • (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
  • 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
redis-check-aof 实验
  1. 配置中开启 aof 功能
  2. 添加数据,生成aof 记录,并shudown Redis 服务
127.0.0.1:6379> set n1 v1
OK
127.0.0.1:6379> set n2 v2
OK
127.0.0.1:6379> set n3 v3
OK
127.0.0.1:6379> set n4 v4
OK
127.0.0.1:6379> shutdown
  1. 备份appendonly.aof 并打开,可以看到日志都有正常写入
*2
$6
SELECT
$1
0
*3
$3
set
$2
n1
$2
v1
*3
$3
set
$2
n2
$2
v2
*3
$3
set
$2
n3
$2
v3
*3
$3
set
$2
n4
$2
v4
  1. 删除最后一行 v4
  2. 执行 redis-check-aof 检查文件
$ redis-check-aof --fix appendonly.aof
0x              87: Expected to read 4 bytes, got 0 bytes
AOF analyzed: size=135, ok_up_to=110, diff=25
This will shrink the AOF from 135 bytes, with 25 bytes, to 110 bytes
# 上面检查出问题,询问是否确定执行 fix 操作
Continue? [y/N]: y
# 通过 truncated AOF,成功执行 fix。
Successfully truncated AOF
  1. diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,可以看到备份的文件多了一个 set n4 v4 的操作日志。
$ diff -u appendonly.aof appendonly.aof.bak 
--- appendonly.aof	2020-08-14 18:59:56.155575737 -0700
+++ appendonly.aof.bak	2020-08-14 18:43:46.463974109 -0700
@@ -24,3 +24,10 @@
 n3
 $2
 v3
+*3
+$3
+set
+$2
+n4
+$2
+v4
  1. $ redis-server /etc/redis/6379.conf 重启 Redis 服务,并查看记录会少了 n4
127.0.0.1:6379> scan 0
1) "0"
2) 1) "n1"
   2) "n3"
   3) "n2"

由于操作系统的缓存机制,数据并没有立即写入硬盘,而是进入了硬盘的缓存,默认系统每30秒会执行一次同步操作,以将缓存中的数据真正地写入硬盘,在这30的过程中如果异常退出则会导制硬盘缓存中的数据丢失,设置同步的时机:

appendfsync everysec|always|no

everysec即每秒执行一次同步操作,always表示每次执行写入都会执行同步,no表示交同操作系统来做即30秒一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值