4.0、持久化:RDB和AOF

429 阅读10分钟

一、 持久化

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
  • 你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

二、RDB

优点
  • RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
  • 非常适用于灾难恢复.
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
缺点
  • 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
  • RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
工作流程

当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  1. Redis 调用forks. 同时拥有父进程和子进程。
  2. 子进程将数据集写入到一个临时 RDB 文件中
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。

redis的fork
1、linux的父子线程
  • 父进程是可以让子进程看到数据
  • 子线程修改数据,父进程的数据还是不变的,父进程修改数据,子进程的数据是不变的
举个例子

父进程有一个数据  a = 3
子进程是可以获取  a = 3

当父进程将数据修改为  a = 8
子进程获取的还是还是  a = 3


当子进程将数据修改为  a = 1
父进程获取的数据还是  a = 8



以上功能的实现主要是依靠写时复制(copy-on-write)机制
该机制的实现方式
1、父进程fork一个子进程的时候,其实是将父进程中对内存的指针拷贝一份到子进程中 这样父子进程都能获取在内存中的值

2、父进程将内存中的值进行修改的时候(写的操作),父进程会先将内存中的那个值拷贝出来一份,然后修改自身的指针,指向拷贝的那一份数据,然后再进行修改,这样子进程的数据是不会修改的,因为子进程还是指向的原来的值

3、子进程将内存中的值进行修改的时候(写的操作),也是重复上一步的动作,这样父进程的值也不会发生修改

2、Redis在使用copy-on-write的形式

redis.png

Redis中的RDB配置
默认的配置

save 900 1    ----> 在900秒后,如果至少更改了1个键,则进行RDB
save 300 10    ---> 在300秒后,如果至少更改了10个键,则进行RDB
save 60 10000   -->在60秒后,如果至少更改了1000个键,则进行RDB

--------------------------------------------------------------------------
rdbcompression yes ----> 开启压缩

-------------------------------------------------------------------------

dbfilename dump.rdb  ---> 保存到磁盘上的文件  注意 保存新的时候,回将旧的删除掉,所以自己可以做数据的备份

-------------------------------------------------------------------------

dir /var/lib/redis/6379  --->将上面的文件保存到哪个木库下  不需要指定文件的名称 因为上面已经定义了

三、AOF

优势
  • AOF,英文是Append Only File,即只允许追加不允许改写的文件,所以不需要寻址,可以带来更高的数据安全性
  • AOF方式是将执行过的写指令记录下来,在数据恢复时,按照从前到后的顺序再将指令都执行一遍
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写(后台重写,不影响客户端的读写),即 Redis 以 append 模式不断的将修改数据写入到老的磁盘文件中,同时 Redis 还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行 rewrite 切换时可以更好的保证数据安全性
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单
弊端
  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为要将命令写到 AOF 缓冲区。此外,还要定时 fsync 一次日志文件
  • 通过回放 AOF 日志中的写入指令来重新构建整个数据集,数据恢复速度比较慢
AOF的日志重写
  • AOF的重写是为了解决aof采用文件追加的方式,文件越来越大的情况
  • 4.0 之前,删除抵消的命令,合并重复的命令,最终重写为一个纯指令的文件
  • 4.0之后,aof通过重写的操作创建除了一个同时包含RDB数据和AOF数据的AOF文件,其中RDB数据位于aof文件的开头,它们储存了服务器开始执行重写操作时的数据库状态,至于那些在重写操作执行之后执行的 Redis 命令, 则会继续以 AOF 格式追加到 AOF 文件的末尾, 也即是 RDB 数据之后。
重写原理
  • AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:
  • Redis 执行 fork() ,现在同时拥有父进程和子进程
  • 子进程开始将新 AOF 文件的内容写入到临时文件
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾
  • 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。
配置文件
默认配置

appendonly no  -->:是否开启AOF,如果开启AOF,那么后期只会用AOF进行数据恢复,4.0版本之后,AOF中包含RDB全量,增加记录写的操作

-------------------------------------------------------------------------
appendfilename "appendonly.aof" --->文件的名称

-------------------------------------------------------------------------
# appendfsync always -->每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全
appendfsync everysec---> 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性
# appendfsync no ------>从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。

-------------------------------------------------------------------------

# 当redis对AOF的文件使用一个子进程进行重写,也就是bgrewriteaof机制,从而不阻塞主程对其余命令的处理,同时解决了AOF文件过大的问题
#问题出现了:同时执行bgrewriteaof操作和主程写aof文件,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘的操作,这样就会造成主程在写aof文件的时候出现阻塞情况
#这个参数就是为了解决这个问题产生的
no-appendfsync-on-rewrite no -->设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的情形,设置为yes,说明并没有执行磁盘操作,只是写入了缓冲区,因此不会造成阻塞,没有磁盘竞争
#如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据

-------------------------------------------------------------------------
#AOF重写触发的条件
auto-aof-rewrite-percentage 100--> 比例
auto-aof-rewrite-min-size 64mb---> 基准值

#aof文件的体积大于64MB,并且aof文件的体积比上一次重写之后体积大了100%的时候,Redis将执行BGREWRITEAOF命令。

-------------------------------------------------------------------------

aof-load-truncated yes--> 设置为yes,那么一个被截断的AOF文件将会为加载,redis服务启动会发出一个日志告知用户这个时间,设置为no,服务器终止一个错误并拒绝启动

-------------------------------------------------------------------------
aof-use-rdb-preamble yes  --->开启RDB和aof混合的方式

四、过期和持久化

  • Keys的过期时间使用Unix时间戳存储(从Redis 2.6开始以毫秒为单位)。这意味着即使Redis实例不可用,时间也是一直在流逝的
  • 数据恢复的时候,如果发现该值的时间已经过期,这个key值会立即失效
在复制AOF文件时如何处理过期
  • 为了获得正确的行为而不牺牲一致性,当一个key过期,DEL将会随着AOF文字一起合成到所有附加的slaves
  • 在master实例中,这种方法是集中的,并且不存在一致性错误的机会。
  • 然而,当slaves连接到master时,不会独立过期keys(会等到master执行DEL命令),他们任然会在数据集里面存在,所以当slave当选为master时淘汰keys会独立执行,然后成为master