目录
- 持久化
- Redis持久化
- redis持久化的方式
- 持久化策略的设置
- 1. RDB(快照)
- fork(多进程)
- RDB配置
- 触发RDB备份
- 自动备份
- 手动执行命令备份(save | bgsave)
- flushall命令
- 主从同步触发
- 动态停止RDB
- RDB 文件恢复
- 验证 RDB 文件是否被加载
- RDB 优缺点
- 优点
- 缺点
- RDB持久化过程
- 2. AOF(文件追加)
- 执行流程
- 写入机制
- 写入缓存
- 重写机制
- 自动触发AOF重写
- 整体流程
- AOF配置
- AOF的备份恢复
- 重写流程
- 完整流程
- 小结
- AOF优缺点
- 优点
- 缺点
- 面试题:AOF和RDB对比
- 3. 混合持久化
- 混合持久化原理
- 混合持久化配置
- 混合持久化优缺点
- 优点
- 缺点
- 混合持久化流程
持久化
- 结合之前学习的JDBC,持久化就是将数据从内存保存到磁盘的过程,其目的就是为了防止数据丢失。
- 因为Redis 的数据全部都存储在内存 中,那么就存在一种情况——如果突然宕机,数据就会全部丢失。因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的 持久化机制,其解决目标就是——将内存中的数据库状态保存到磁盘中。
Redis持久化
简单来说,从客户端发起请求开始,到服务器真实地写入磁盘,需要发生如下几件事情:
- 客户端向数据库 发送写命令 (数据在客户端的内存中)
- 数据库 接收 到客户端的 写请求 (数据在服务器的内存中)
- 数据库 调用系统 API 将数据写入磁盘 (数据在内核缓冲区中)
- 操作系统将 写缓冲区 传输到 磁盘控控制器 (数据在磁盘缓存中)
- 操作系统的磁盘控制器将数据 写入实际的物理媒介 中 (数据在磁盘中)
redis持久化的方式
- 快照方式(RDB,RedisDataBase):将某个时刻的内存数据,以二进制的方式写入磁盘;由于是二进制写入磁盘,因此效率较高,但是一旦 Redis 意外终止,就会导致数据部分丢失;
- 文件追加方式(AOF,AppendOnlyFile):记录所有的操作命令,并以文本的形式追加到文件中;由于是文本形式写入,因此效率较低,又由于 AOF 会记录操作指令,因此保证了数据的完整性。
- 混合持久化方式:Redis 4.0 之后新增的⽅式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前数据以 RDB 形式写入文件的开头,在将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能加降低数据丢失的风险。
持久化策略的设置
在连接服务器终端之后使用 redis-cli 命令开启 Redis 客户端,通过以下命令使用不同的持久化策略:
- RDB(快照):当 AOF 和混合持久化都没有开启的情况下默认是 RDB 持久化策略;
- AOF(文件追加):在没有开启混合持久化的情况下,使用
config set appendonly yes
开启; - 混合持久化:使用
config set aof-use-rdb-preamble yes
直接开启混合持久化。
1. RDB(快照)
RDB(Redis DataBase)是将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘的过程。
- Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写
- 在服务线上请求的同时,Redis 还需要进行内存快照,内存快照要求 Redis 必须进行文件 IO 操作,这意味着单线程同时在服务线上的请求还要进行文件 IO 操作,文件 IO 操作会严重拖垮服务器请求的性能
- 还有个重要的问题是为了不阻塞线上的业务,就需要边持久化边响应客户端请求。
- 持久化的同时,内存数据结构还在改变,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它给删掉了,还没持久化完怎么办?Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。多进程 COW 也是鉴定程序员知识广度的一个重要指标。
fork(多进程)
- Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。
- 子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。
- 这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。
- 随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长。但是也不会超过原有数据内存的 2 倍大小。另外一个 Redis 实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页面。每个页面的大小只有 4K,一个 Redis 实例里面一般都会有成千上万的页面。
- 子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安心的遍历数据了进行序列化写磁盘了
RDB配置
- 在redis.conf中,可以修改rdb备份文件的名称,默认为dump.rdb
- 在redis.conf中,rdb文件的保存的目录是可以修改的,默认为Redis启动命令所在的目录,如下
触发RDB备份
自动备份
- 需配置备份规则。可在redis.conf中配置自动备份的规则
- 格式: save m n
- 是指在 m 秒内,如果有 n 个键发生改变,则自动触发持久化。
- 参数 m 和 n 可以在 Redis 的配置文件中找到
- 例如, save601 则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化。
- 自动触发持久化,本质是 Redis 通过判断,如果满足设置的触发条件,自动执行一次 bgsave 命令。
- 注意:当设置多个 save m n 命令时,满足任意一个条件都会触发持久化。
- 例如,我们设置了以下两个 save m n 命令
save 60 10 save 600 1
- 当 60s 内如果有 10 次 Redis 键值发生改变,就会触发持久化;如果 60s 内 Redis 的键值改变次数少于 10 次,那么 Redis 就会判断 600s 内,Redis 的键值是否至少被修改了一次,如果满足则会触发持久化。
save 20 3
手动执行命令备份(save | bgsave)
-
save:save时只管保存,其他不管,全部阻塞,手动保存,在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用。
-
bgsave:redis会在后台异步进行快照操作,快照同时还可以响应客户端情况。它和 save 命令最大的区别就是 bgsave 会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了,相对于整个流程都阻塞的 save 命令来说,显然 bgsave 命令更适合我们使用
可以通过 lastsave 命令获取最后一次成功生成快照的时间(获取到的是时间戳)。
flushall命令
执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义。
主从同步触发
在 Redis 主从复制中,当从节点执行全量复制操作时,主节点会执行 bgsave 命令,并将 RDB 文件发送给从节点,该过程会自动触发 Redis 持久化。
动态停止RDB
redis-cli config set save "" #save后给空值,表示禁用保存策略
RDB 文件恢复
- 当 Redis 服务器启动时,如果 Redis 根目录存在 RDB 文件 dump.rdb,Redis 就会自动加载 RDB 文件恢复持久化数据。如果根目录没有 dump.rdb 文件,请先将 dump.rdb 文件移动到 Redis 的根目录。
验证 RDB 文件是否被加载
- Redis 在启动时有日志信息,会显示是否加载了 RDB 文件,我们执行 Redis 启动命令:
src/redis-server redis.conf
Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
RDB 优缺点
优点
- RDB 内容为二进制数据,占用内存小,更紧凑,适合作为备份文件。
- RDB 对灾难恢复非常有用,他是一个紧凑的文件,可以更快的传输到远程服务器进行 Redis 服务。
- RDB 可以提高 Redis 的运行速度,每次持久化 Redis 主进程都会 fork() 一个子进程,进行数据持久化到磁盘,Redis 主进程不会执行磁盘 I/O 等操作。
- 与 AOF 格式文件相比, RDB 文件可以更快的重启,因为文本形式写入磁盘效率低于二进制方式写入磁盘。
缺点
- 由于 RDB 只能保存某个时间段的数据,因此一旦中途 Redis 服务意外终止,则会丢失一段时间的 Redis 数据。
- Fork的时候,内存中的数据会被克隆一份,大致2倍的膨胀,需要考虑
- 虽然Redis在fork的时候使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down的话,就会丢失最后一次快照后所有修改
RDB持久化过程
2. AOF(文件追加)
- AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。
- 它的工作方式非常简单:每次执行 修改内存 中数据集的写操作时,都会 记录 该操作。假设 AOF 日志记录了自 Redis 实例创建以来 所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例 顺序执行所有的指令,也就是 「重放」,来恢复 Redis 当前实例的内存数据结构的状态。
执行流程
- 命令追加:将Redis的写命令追加到缓冲区aof_buf中
- 文件写入和文件同步:根据不同的同步策略同步到文件中
- 文件重写:定期触发文件重写,达到压缩文件的目的命令追加:
Redis先将写命令追加到缓冲区中,而不是每次都直接写入到文件中,如果每次都把用户的命令直接写入到文件中,会有很大的磁盘IO的压力
命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点
写入机制
- Redis 在收到客户端修改命令后,先进行相应的校验
- 如果没问题,就立即将该命令存追加到 .aof 文件中,也就是先存到磁盘中,然后服务器再执行命令。
- 这样就算遇到了突发的宕机情况情况,也只需将存储到 .aof 文件中的命令,进行一次“命令重演”就可以恢复到宕机前的状态。
写入缓存
- 在上述执行过程中,有一个很重要的环节就是命令的写入,这是一个 IO 操作。
- Redis 为了提升写入效率,它不会将内容直接写入到磁盘中,而是将其放到一个内存缓存区(buffer)中,等到缓存区被填满时采用异步真正将缓存区中的内容写入到磁盘里。
- 这就意味着如果机器突然宕机,AOF 日志内容可能还没有来得及完全刷到磁盘中,这个时候就会出现日志丢失。那该怎么办?——Redis 为数据的安全性考虑,同样为 AOF 持久化提供了策略配置
- Always:服务器每写入一个命令,就调用一次 fsync函数,将缓冲区里面的命令写入到硬盘。这种模式下,服务器出现故障,也不会丢失任何已经成功执行的命令数据,但是其执行速度较慢;
- Everysec(默认):服务器每一秒调用一次 fsync 函数,将缓冲区里面的命令写入到硬盘。这种模式下,服务器出现故障,最多只丢失一秒钟内的执行的命令数据,通常都使用它作为 AOF 配置策略;
- No:服务器不主动调用 fsync 函数,由操作系统决定何时将缓冲区里面的命令写入到硬盘。这种模式下,服务器遭遇意外停机时,丢失命令的数量是不确定的,所以这种策略,不确定性较大,不安全。
- 备注:Linux 系统的 fsync() 函数可以将指定文件的内容从内核缓存刷到硬盘中。
- 由于是 fsync 是磁盘 IO 操作,所以它很慢!如果 Redis 执行一条指令就要 fsync 一次(Always),那么 Redis 高性能将严重受到影响。
- 在生产环境的服务器中,Redis 通常是每隔 1s 左右执行一次 fsync 操作( Everysec),这样既保持了高性能,也让数据尽可能的少丢失。最后一种策略(No),让操作系统来决定何时将数据同步到磁盘,这种策略存在许多不确定性,所以不建议使用。
重写机制
- Redis 在长期运行的过程中,aof 文件会越变越长。如果机器宕机重启,“重演”整个 aof 文件会非常耗时,导致长时间 Redis 无法对外提供服务。因此就需要对 aof 文件做一下“瘦身”运动。
- 为了让 aof 文件的大小控制在合理的范围内,Redis 提供了 AOF 重写机制,手动执行
BGREWRITEAOF
命令127.0.0.1:6379> BGREWRITEAOF Background append only file rewriting started
- 通过 bgrewriteaof 操作后,服务器会生成一个新的 aof 文件,该文件具有以下特点
- 新的 aof 文件记录的数据库数据和原 aof 文件记录的数据库数据完全一致;
- 新的 aof 文件会使用尽可能少的命令来记录数据库数据,因此新的 aof 文件的体积会小很多;
- AOF 重写期间,服务器不会被阻塞,它可以正常处理客户端发送的命令。
- 即使 Bgrewriteaof 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 Bgrewriteaof 成功之前不会被修改
自动触发AOF重写
Redis 为自动触发 AOF 重写功能,提供了相应的配置策略。如下所示:修改 Redis 配置文件,让服务器自动执行 BGREWRITEAOF
命令
#默认配置项
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb #表示触发AOF重写的最小文件体积,大于或等于64MB自动触发。
该配置项表示:触发重写所需要的 aof 文件体积百分比,只有当 aof 文件的增量大于 100% 时才进行重写,也就是大一倍。比如,第一次重写时文件大小为 64M,那么第二次触发重写的体积为 128M,第三次重写为 256M,以此类推。如果将百分比值设置为 0 就表示关闭 AOF 自动重写功能。
整体流程
- 客户端的请求写命令会被append追加到AOF缓冲区内
- AOF缓冲区会根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中
- AOF文件大小超过重写策略或手动重写时,会对AOF文件进行重写(rewrite),压缩AOF文件容量
- redis服务器重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的
AOF配置
AOF默认不开启,可以在 redis.conf 文件中对AOF进行配置开启:
appendonly no # 是否开启AOF,yes:开启,no:不开启,默认为no
appendfilename "appendonly.aof" # aof文件名称,默认为appendonly.aof
dir ./ # aof文件所在目录,默认./,表示执行启动命令时所在的目录
AOF的备份恢复
- 正常恢复:
- 修改默认的appendonly no,改为yes
- 将有数据的aof文件复制一份保存到对应的目录(查看目录:config get dir)
- 恢复:重启redis然后重新加载
- 异常恢复:
- 修改默认的appendonly no,改为yes
- 如遇到aof文件损坏,通过 redis-check-aof --fix appendonly.aof 进行恢复,appendonly.aof是文件名
重写流程
- 手动执行 bgrewriteaof命令触发重写,判断是否当前有bgfsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞
- 子进程遍历redis内存中的数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整性以及新AOF文件生成期间的新的数据修改动作不会丢失
- 子进程写完新的AOF文件后,向主进程发送信号,父进程更新统计信息
- 主进程把aof_rewrite_buf中的数据写入到新的AOF文件
- 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写
- no-appendfsync-on-rewrite:重写时,不会执行appendfsync操作
- 该参数表示在正在进行AOF重写时不会将AOF缓冲区中的数据同步到旧的AOF文件磁盘,也就是说在进行AOF重写的时候,如果此时有写操作进来,此时写操作的命令会放在aof_buf缓存中(内存中),而不会将其追加到旧的AOF文件中,这么做是为了避免同时写旧的AOF文件和新的AOF文件对磁盘产生的压力。
- 默认是ON,表示关闭,即在AOF重写时,会对AOF缓冲区中的数据做同步磁盘操作,这在很大程度上保证了数据的安全性。但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)
- 如果no-appendfsync-on-rewrite为yes,不写入aof文件,只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)
- 但在数据量很大的场景,因为两者都会消耗磁盘IO,对磁盘的影响较大,可以将其设置为“yes”减轻磁盘压力,但在极端情况下可能丢失整个AOF重写期间的数据。
完整流程
小结
- AOF文件是一个只进行追加的日志文件
- Redis可以在AOF文件体积变得过大时,自动地在后台对AOF文件进行重写
- AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松。
- 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积
根据所使用的fsync策略,AOF的速度可能会慢于RDB
AOF优缺点
优点
- AOF 持久化保存的数据更加完整。因为 AOF 提供了三种保存策略:“每次操作保存、每秒种保存一次、跟随系统的持久化策略保存”,其中每秒保存一次,从数据的安全性和性能两方面考虑都是个不错的选择,也是 AOF 的默认策略,即使发生意外,最多丢失 1s 钟的数据;
- AOF 采用命令追加的写入方式,所有不会出现文件损坏问题,即使由于意外情况,也可以使用 redis-check-aof 工具轻松修复;
- AOF 持久化文件,跟容易解析,因为他是把所有 Redis 键值操作命令,以文件的方式存入磁盘,即使不小型使用 flushall 命令删除了所有的键值信息,只要使用 AOF 文件,删除最后的 flushall 命令,重启 Redis 即可恢复之前误删的数据。
缺点
- 对于相同数据集来说,AOP 文件要大于 RDB文件。
- 在 Redis 负载比较高的情况下, RDB 比 AOF 性能更好。
- RDB 使用快照持久化 Redis 数据,而 AOF 使用命令追加到 AOF 文件中,因此, RDB 比 AOF 更健壮。
面试题:AOF和RDB对比
RDB | AOF |
---|---|
全量备份,一次保存整个数据库 | 增量备份,一次只保存一个修改数据库的命令 |
每次执行持久化操作的间隔时间较长 | 保存的间隔默认为1秒钟(Everysec) |
数据保存为二进制格式,还原速度快 | 使用文本格式还原数据,速度一般 |
执行save命令时会阻塞服务器,但手动或者自动触发的bgsave不会阻塞服务器 | 无论何时都不会阻塞服务器 |
3. 混合持久化
- 生产环境中一般采用两种持久化机制混合使用。
- 将内存中数据快照存储在AOF文件中(模拟RDB),后续再以AOF追加方式。
- 如果仅作为缓存使用,可以承受几分钟数据丢失,可以使用RDB,对主程序性能影响最小。
混合持久化原理
- 重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。
- Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小
- 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升
混合持久化配置
在redis的配置文件当中有一个aof-use-rdb-preamble
参数来开启 混合持久化,默认是yes开启的。混合持久化结合了 RDB 和 AOF 的优点,Redis 5.0 默认是开启的。
混合持久化优缺点
优点
- 结合了 RDB 和 AOF 持久化的优点,开头为 RDB 格式,使得 Redis 可以跟快启动,同时结合 AOF 优点,降低了大量丢失数据的风险。
缺点
- AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件可读性变的更差。
- 兼容性差。混合持久化 AOF 文件,就不能用在 Redis 4.0 之前的版本了。