redis持久化,就是要在内存上存数据,硬盘上也存数据。
当写数据时, 内存和硬盘同时写。
读数据时,读内存中的数据。
硬盘的数据是在redis重启时,用来恢复内存中的数据。(可以理解为redis内存数据的备份)
代价是消耗了更多的空间(同一份数据存储了两遍)
Redis ⽀持 RDB (Redis Database)和 AOF (Append Only File)两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题, 当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。
RDB策略是定期备份,而AOF策略是实时备份。
RDB
RDB 持久化是定期 把当前进程数据生成快照 保存到硬盘的过程,触发 RDB 持久化过程分为手动触发 和 自动触发。
手动触发
⼿动触发分别对应 save 和 bgsave 命令:
• save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为⽌,对于内存⽐较⼤的实例造成⻓间阻塞,基本不建议采⽤。
• bgsave 命令:Redis 进程执⾏ fork 操作创建⼦进程,RDB 持久化过程由⼦进程负责,完成后⾃动 结束。阻塞只发⽣在 fork 阶段,⼀般时间很短。(这里的bg就是background,就是在背后出来触发)

1、判断目前是否有多个客户端执行bgsava,如有多个就立即返回。因为都是保存的同一份数据,只执行一个bgsava 就行了
2、然后在fork出一个子进程出来(相当于redis server的克隆体,如果redis server中存的数据比较多,占用内存很大,fork利用写实拷贝的机制,也不会有过多内存的消耗。因为在bgsave这个场景中,绝大部分的内存数据是不需要改变的)
3、然后由子进程负责进行 写文件,生成快照过程。父进程继续接收客户端的请求,正常提供服务
4、子进程完成持久化之后,再通过信号通知父进程。父进程更新一些统计信息,然后子进程就可以销毁了
自动触发
在redis配置文件中设置一下,让redis每隔多长时间或者每产生多少次修改 就自动触发。
在目录 /etc/redis下的redis.conf配置文件中进行设置。
rdb镜像文件
redis生成的rdb文件,是存放在redis的工作目录中的。也就是在redis配置文件中进行设置的。

然后到这个目录下去,查看下

redis服务器是默认开启了rdb机制的。
这个rdb文件是一个二进制文件。把内存中的数据,以压缩的形式,保存到这个二进制文件中。这个尽量避免去修改rdb,如果改坏了, 当redis服务器重启之后,去读取rdb文件就会出现格式错误。
为此,redis提供了rdb校验工具

当执行 生成 rdb镜像 操作的时候,
1、会把要生成的快照数据,先保存至一个临时文件。当快照生成完毕
2、再删除之前的rdb文件
3、再把新生成的临时rdb文件改为刚刚的dump.rdb。
这样就保证了dump.rdb文件自始至终就只有一个
rdb演示
进入redis客户端,执行命令 redis-cli -h 127.0.0.1 -p 6379
然后查看rdb文件,执行命令 vi /var/lib/redis/dump.rdb

然后在redis客户端插入数据,再次查看rdb文件


此时rdb文件依旧和插入数据之前是一样的。因为现在并未执行手动触发rdb,也没有达到自动触发rdb的条件。
自动触发rdb的条件,也是在 目录 /etc/redis下的redis.conf配置文件中进行设置的。

也就是说配置自动触发的条件,就可以在redis.conf中进行配置。
但是生成一次rdb快照的成本是一个比较高的成本,不能让这个操作太频繁。
这也导致快照的数据和实时内存中的数据可能存在偏差。比如说在两次快照之间的时候,redis服务器挂了,就会导致在快照之间这个时间段的数据就丢失了,因为还并未写到磁盘。(AOF就是解决这种场景的有效方案)
操作1
操作:手动执行下sava(不推荐)或者bgsava,手动触发rdb

以为目前数据少,执行bgsave就立即完成了,立即查看应该就有结果。
但是当数据比较多的时候,执行bgsave可能就需要消耗一定时间,立即查看不一定就是生成完毕了的。
然后在查看rdb文件

虽然dump.rdb文件是二进制的,但是key还是能看出来的。
然后重启下redis,看下效果。
执行命令 service redis-server stop service redis-server start
进入redis客户端查看key

操作2
操作:插入新值,不手动执行bgsave
插入新值

查看rdb文件

并没有key1
然后重启redis,进入客户端查看是否还有新设置的值

查看rdb文件

redis生成快照操作,不仅仅是手动执行命令才触发,也能自动触发
自动触发的方式:
1、通过配置文件中的save 执行 M时间内,修改N次
2、通过shutdown 命令(redis里面的一个命令)关闭redis服务器,也会自动触发(service redis-server restart)(正常关闭)
3、redis进行主从复制的时候,主节点也会自动生成rdb快照,然后再将快照文件内容传递给从节点
再设置新值,通过kill -9命令关闭redis,模拟redis server异常关闭

由于是由service启动的redis,ubuntu会有守护进程观察redis服务的情况,如果redis挂了,就会自动拉起一个新的。这里的进程id并不相同,也就是redis进行了重启过了
然后进入redis客户端

这里并没有刚刚的设置的key2。
总结
如果通过正常的流程重新启动redis服务器,此时redis会在退出的时候,自动触发生成rdb操作。但是如果是异常重启,比如 kill -9或者 服务器掉电,此时redis服务器来不及生成rdb文件,内存中尚未保存至快照的数据就会随着异常重启丢失掉。
操作3
操作:查看rdb文件的保存过程
先查看当前rdb文件信息,观察其中的inode编号,执行命令 stat dump.rdb

然后执行一次bgsave

再次查看rdb文件的inode编号

可以看到两次的inode编号并不相同,说明rdb文件已经不是之前的rdb文件了,而是更新后的。
文件的典型组织方式(ext4)主要是把整个文件系统分为了三个大的部分
1、超级块(存放的是整个文件系统的管理信息和元数据。)
2、inode区(存放着inode节点,每个文件都会分配一个inode数据结构,包括了文件的各种元数据,和该文件数据存储在哪些block中)
3、block区(存放了文件具体的数据内容)
操作4
操作:通过配置自动生成rdb快照
执行flushall 也会情况rdb文件。
修改 配置文件 /etc/redis/redis.conf

然后重启redis服务,配置才会生效
重启,执行命令 service redis-server restart
然后在redis客户端设置数据

然后1分钟之后,查看dump.rdb文件

可以看到RDB文件中已经把新增的key保存下来了
如果在配置文件中写入 save "",表示关闭自动触发RDB机制
操作5
操作:将rdb文件改坏,再通 kill -9重启redis
如果使用命令 service redis-server restart 重启redis,redis会在退出时,自动触发rdb快照,将改坏的rdb替换为正常的rdb文件了
改坏rdb文件

执行命令 kill -9重启redis

查看redis的日志,执行命令 vi /var/log/redis/redis-server.log

可以看到是因为恢复数据过程中,rdb文件读取错误。
总结
rdb改动的位置,如果在内容之前,redis启动不了。如果改在内容之后,redis是能启动的,数据也是能读到的。
但是rdb文件改动之后,就是一个错误文件,为此需要使用redis提供的工具进行检查rdb文件格式是否符合要求。
redis提供的工具在目录 /usr/bin下

检查工具和redis服务器,在5.0版本是同一个可执行程序,可以在运行时加入不同选项,而达到使用不同功能。
运行的时候,加入rdb文件作为命令行参数,此时就是以检查工具的方式来运行,而不会真的启动 redis 服务器
检查rdb文件,执行命令 redis-check-rdb dump.rdb

rdb的优缺点
• RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中 (如 hdfs)⽤于灾备。
• Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。
• RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进 程,属于重量级操作,频繁执行成本过高。
• RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有风险。
• RDB最大的问题,不能实时的持久化保存数据。在两次生成快照之间,实时的数据可能会随着重启而丢失。
AOF
AOF(Append Only File)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作⽤是解决了数据持久化的实时性,⽬前已经是Redis 持久化的主流⽅式。理解掌握好 AOF 持久化机制对我们兼顾数据安全性和性能⾮常有帮助。
开启AOF的时候,RDB就不生效了。默认AOF是关闭状态,需要修改配置文件进行修改。

开启的话,就将no 改为 yes

appendfilename表示AOF生成的文件名,所在位置是个rdb文件目录一致,都是在 /var/lib/redis下,是可配置的。
配置完之后,需要重启使配置生效。执行命令 service redis-server restart
然后在客户端写入

再查看appendonlydir下的appendonly.aof.1.incr.aof文件

然后再通过kill -9的方式干掉redis重启,再通过redis客户端查看持久化的数据能否被恢复

AOF工作流程
redis是单线程服务器,但是速度很快,因为是操作内存。
但是引入AOF之后,既要写内存,又要写硬盘,还能保持之前的速度吗?
实际上,由于AOF 的工作流程,写磁盘的操作并不会影响redis的性能。AOF 的⼯作流程操作:命令写⼊(append)、⽂件同步(sync)、⽂件重写 (rewrite)、重启加载(load),

-
所有的写⼊命令会追加到 aof_buf(缓冲区)中。
-
AOF 缓冲区根据对应的策略向硬盘做同步操作。
-
随着 AOF 文件越来越⼤,需要定期对 AOF 文件进⾏重写,达到压缩的目的。
-
当 Redis 服务器启动时,可以加载 AOF 文件进行数据恢复。
文件写入
硬盘上读写数据,顺序读写的速度是比较快的(相比内存还是慢得多),而随机访问则速度是比较慢的。
AOF是每次把新的操作写入到原有文件的末尾,属于是顺序写入。
另外,写硬盘的时候,写入硬盘数据的多少,对于性能影响并不大,而写入硬盘的次数影响就比较大了。AOF是先把数据写入缓冲区,在根据一定策略写入硬盘,这样写入硬盘次数就大大减少了。
数据写入缓冲区,本质还是写在内存中的,如果突然进程挂了,或者主机掉电了,缓冲区的数据就丢掉了。
文件同步
Redis 提供了多种 AOF 缓冲区同步⽂件策略,由参数 appendfsync 控制
|----------|-----------------------------------------------------|
| 可配置值 | 说明 |
| always | 命令写⼊ aof_buf 后调⽤ fsync 同步,完成后返回 |
| everysec | 命令写⼊aof_buf 后只执⾏ write 操作,不进⾏ fsync。每秒由同步线程进⾏fsync。 |
| no | 命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制 fsync 频率。 |
系统调⽤ write 和 fsync 说明:
• write 操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性 能。write 操作在写入系统缓冲区后立即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区 页空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
• Fsync 针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。
• 配置为 always 时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤ 约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。
• 配置为 no 时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据 重要程度很低,⼀般不建议配置。
• 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的 数据。

刷新策略频率越高 ,性能影响就越大 ,而数据可靠性就越高。
刷新策略频率越低 ,性能影响就越小 ,而数据可靠性就越低。
重写机制
随着命令不断写⼊ AOF,文件会越来越大,为了解决这个问题,Redis 引⼊ AOF 重写机制压缩文 件体积。AOF ⽂件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF ⽂件。
重写后的 AOF 为什么可以变小?有如下原因:
• 进程内已超时的数据不再写入文件。
• 旧的 AOF 中的⽆效命令,例如 del、hdel、srem 等重写后将会删除,只需要保留数据的最终版 本。
• 多条写操作合并为⼀条,例如 lpush list a、lpush list b、lpush list 从可以合并为 lpush list a b c。
较小的 AOF 文件一方面降低了硬盘空间占用,一方面可以提升启动 Redis 时数据恢复的速度。
AOF 重写过程可以手动触发和自动触发(配置文件中设置):
• 手动触发:调用 bgrewriteaof 命令。
• 自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时 机。
◦ auto-aof-rewrite-min-size:表示触发重写时 AOF 的最小文件大小,默认为 64MB。
◦ auto-aof-rewrite-percentage:代表当前 AOF 占用大小相比较上次重写时增加的比例。
AOF重写流程如下

父进程fork子进程之后,子进程写aof文件的同时,父进程仍然不停地接收客户端的请求,并把这些请求写入到aof_buf缓冲区,再刷新到旧的aof文件中。
在fork子进程的一瞬间,子进程就继承了当初父进程的内存状态,因此子进程里的内存状态是父进程fork之前的状态,对于fork之后新来的请求,对内存造成的修改,子进程是不知道的。
为此,父进程准备了一个aof_rewrite_buf缓冲区,专门方fork之后接收的请求数据,子进程这边在把aof数据写入新AOF文件完成后,会通过信号通知父进程,父进程再将aof_rewrite_buf缓冲区中的内容也写入新的AOF文件中,然后新的AOF文件就可以替代旧的AOF文件了。
如果当前进程正在执⾏ AOF 重写,请求不执⾏。如果当前进程正在执⾏ bgsave 操作,重写命令 延迟到 bgsave 完成之后再执⾏。
重写的时候,不关心aof文件中原来都有什么,只是关系内存中最终的数据状态。
子进程只需要把内存中当前的数据获取出来,以AOF格式写入到一个新的AOF文件中。(内存中的数据状态,就相当于把AOF文件结果整理后的模样了)
思考:
1、RDB对于fork之后,就不对数据不管了,而AOF则是通过aof_rewrite_buf缓冲区的方式进行处理。为什么RDB不采用这样的策略?
RDB本身设计理念就是为了定期备份,而AOF则是实时备份。二者到底是谁更加实用高效呢?则应该划分不同的应用场景。RDB定期备份,对于redis性能影响不多,只有在备份的时候会产生影响,但是对于数据的可靠性就有一定的风险。而AOF虽然是实时备份,尽管采用了策略优化对redis的影响,但是实时备份,就意味着对于redis的影响是一直存在的,但对于数据的可靠性又是很友好的。
2、AOF重写机制fork之后,为什么还需要继续维护旧的aof文件,继续向aof_buf缓冲区写入文件。而不是交给子进程去写新的aof文件,父进程再将aof_rewrite_buf缓冲区的内容追加到新的aof文件,然后去替换旧的aof文件,还能省掉维护旧的aof文件的开销了?
考虑极端,如果子进程写aof文件写到一半,机器挂了,断网了,数据就丢掉了。而父进程继续通过向aof_buf缓冲区写入文件,刷新到旧的aof文件中,重启之后还能对数据进行恢复。
混合持久化
首先在redis客户端上设置数据

在查看aof文件

在redis客户端上执行命令,开启aof重写

再次查看aof文件,发现是和rdb文件一样都是二进制文件。
这是因为redis引入了"混合持久化"的方式,结合rdb和aof的特点。
aof本来是按照文本的方式写入文件的,但是文本的方式写文件,后续redis重启数据加载成本是比较高的。
引入混合持久化之后,按照aof的方式,每个请求和操作都记录入了文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到aof文件中,后续再进行操作,仍然按照aof文本的方式追加到文件后面(也就是前面是二进制,后续记录的请求操作仍然是文本)。
混合持久化的开启,也是在redis.conf文件中去设置的。默认是开启的。

当Redis上同时存在的rdb快照和aof文件的时候,是以aof文件为主(aof中包含的数据比rdb更全),下面是启动时数据恢复的一个流程。

总结:
-
Redis 提供了两种持久化方案:RDB 和 AOF。
-
RDB 视为内存的快照,产⽣的内容更为紧凑,占用空间较小,恢复时速度更快。但产生 RDB 的开销较大,不适合进行实时持久化,⼀般⽤于冷备和主从复制。
-
AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件。
-
RDB 和 AOF 都使⽤ fork 创建⼦进程,利用 Linux 子进程拥有父进程内存快照的特点进行持久化, 尽可能不影响主进程继续处理后续命令。