redis的持久化
Redis ⽀持 RDB 和 AOF 两种持久化机制 ,持久化功能有效地避免因进程退出造成数据丢失问题,当下次重启时利⽤之前持久化的⽂件即可实现数据恢复 。
一、RDB持久化机制
RDB会定期的把我们Redis内存中的所有数据都保存到硬盘中。
RDB有定期保存有两种方式,分为手动触发
和自动触发
1.1手动触发
程序员通过redis客户端,执行特定的命令来触发RDB的保存
手动触发有两个指令,分别是save和bgsave
- save : 执行save指令的时候,redis就会全力以赴的进行保存操作,此时就会阻塞redis的其他客服端的命令,导致类似于keys的后果,一般不建议使用save
- bgsave:bg可理解为background(后面)不会影响redis服务器处理其他客服端的请求和命令
bgsave的非阻塞的实现原理,系统创建子进程来,让子进程去执行bgsave后续的保存操作,父进程继续接受客服端发来的命令。
bgsave的执行过程:
如果客服端执行bgsave命令的时候,发现系统中已经存在一个正在执行的bgsave子进程,那么就用该bgsave子进程的保存结果返回给客服端。
如果客服端执行bgsave命令的时候,发现此时没有正在执行bgsave的子进程,那么就通过fork()这样的系统调用创建出一个子进程,用于执行bgsave的保存操作。
子进程负责进行写文件,父进程继续接受客服端的请求,继续正常提供服务。
当子进程完成整体的持久化过程之后,就会通知父进程,任务完成了,父进程就会更新一些统计信息。然后子进程就可以结束销毁了。
如图:
RDB文件是保存在redis的默认工作目录中的,在配置文件中可以找到默认工作目录
配置文件在/etc/redis/redis.conf中
sudo vim /etc/redis/redis.conf
RDB文件默认名字:dump.rdb
查看该目录:
可以发现RDB文件保存在这。
redis服务器默认就是开启rdb的。
RDB文件时二进制文件,在保存的时候会先通过压缩,然后再存储,该过程会消耗CPU资源,但能节省存储空间。
注意:该文件中的内容不要随便修改,一旦把数据格式改坏了,那就会导致后续redis服务器重新启动的时候出错,因为重启时会尝试加载这个RDB文件,如果发现格式错误,就可能会导致加载数据失败。
RDB文件的检查工具:
RDB持久化操作是可以多次触发的原因
当执行生成RDB镜像操作的时候,此时就会把要生成的快照数据,先保存在一个临时文件中,当这个快照生成完毕之后,在删除之前的RDB文件,把新生成的临时的RDB文件名字改成刚才的dump.rdb文件。
1.2自动触发
①自动触发在满足条件的情况下才会发生,配置文件中有RDB自动触发的条件,该条件可修改。
默认条件是,900秒内修改一次或300秒内修改10次或60秒内修改10000次,时间和次数需要同时满足才会触发。
当把条件设置为 save ”“
,就表示禁止上面的条件生成快照,但后面说到的两个条件,不受影响。
注意:
- 虽然该条件可以修改,但是不能将条件修改得频繁触发,因为每次触发生成一次快照的成本都比较高。
- 也正因为如此RDB生成的不能太频繁,就导致了快照中的数据和实际的数据情况可能存在一定的偏差。
②自动触发还可以通过shutdown命令(redis中的一个命令)关闭redis服务器,也会触发(service redis-server restart)
③redis进行主从复制的时候,主节点也会自动生成RDB快照,然后把RDB快照文件内容传输给从节点(后面博客详细说明)
1.3利用RDB文件恢复redis中的内容
redis在关闭以后,内存中的内容就都释放了,当再重启的时候想要恢复原来的内容那么就需要读取RDB文件中的数据来恢复。
演示:
首先我们先创建三条key-value值,然后使用bgsave命令将它们保存到RDB文件中。
然后让redis服务器重启
最后登录redis,查看结果:
可以发现和我们预期的结果一样。
注意:
- 当我们未使用bgsave命令且自动触发条件未满足,此时使用了kill -9 命令让redis服务器异常终止,那么内存中的数据未来得及保存到RDB文件中,该数据就会丢失。
- 在执行完快照操作以后,前RDB文件和现RDB文件不是同一个文件,因为它们的inode不同。
当我们把RDB文件的部分改坏了,redis启动时使用该二进制文件得到的结果是不可预期的,那么redis服务器可能会不能够启动,启动了以后数据也可能是错误的。
1.4 RDB的优缺点
优点:
-
RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中(如 hdfs)⽤于灾备。
-
Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。
缺点:
-
RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。
-
RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛险。
二、AOF的持久化机制
以独⽴⽇志的⽅式记录每次写命令,重启时再重新执⾏ AOF ⽂件中的命令达到恢复数据的⽬的。AOF 的主要作⽤是解决了数据持久化的实时性,⽬前已经是 Redis 持久化的主流⽅式。理解掌握好 AOF 持久化机制对我们兼顾数据安全性和性能⾮常有帮助。
2.1使用AOF
AOF默认是关闭状态的,想用使用我们需要去配置文件手动开启:
将appendonly 设置为yes即可,appendfilename 代表的是AOF文件的默认名称
此时将RDB文件删除掉,然后重启一下redis服务器:
此时再查看工作目录,可以发现AOF文件已经存在了:
现在我们插入几条数据:
然后查看AOF中的内容:
可以发现AOF文件中保存的都是我们输入的指令。
AOF是一个文本文件,每次进行的操作都会被记入到AOF文件中,用一些分隔符进行分隔。
2.2 AOF的执行过程
AOF机制不是直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区中,积累一波数据后,再统一写入硬盘中。
AOF 过程中为什么需要 aof_buf 这个缓冲区?
Redis 使⽤单线程响应命令,如果每次写 AOF ⽂件都直接同步硬盘,性能从内存的读写变成 IO 读写,必然会下降。先写⼊缓冲区可以有效减少 IO 次数,同时,Redis 还可以提供多种缓冲区同步策略,让⽤⼾根据⾃⼰的需求做出合理的平衡。
如果在缓冲区中的数据没来得及写入硬盘,那么数据是否丢了?
是的
所以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 秒的数据。
2.3 AOF的重写机制
随着命令不断写⼊ 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的重写可分为手动触发和自动触发
手动触发
调用bgrewriteaof命令进行重写。重写的时候不关心aof文件中原来都有什么,只关心内存中最终的数据状态。
**bgrewriteaof命令执行过程:**父进程接收到bgrewriteaof命令时,会调用fork()系统接口创建一个子进程,子进程只需要把内存中当前的数据获取出来,以AOF的格式写入到一个新的AOF文件中,父进程仍然继续接受客服端发来的命令,当fork后仍然有新的命令发来,那么父进程会维持一个aof_rewrite_buf缓冲区和aof_buf缓冲区,将fork()后的命令存放到该缓冲区中,再把aof_buf缓冲区中的值写入旧的缓冲区中,等到子进程处理完毕以后,父进程就会把新的AOF文件和aof_rewrite_buf缓冲区合并,再把旧的AOF文件和替换掉,此时保存在磁盘中的AOF文件就是我们要的文件了。
如果在执行bgrewriteaof的时候,当前redis正在进行aof重写,会怎么样呢?
此时不会再次执行新的aof重写,会将当前正在执行的这次重写作为结果返回。
如果在执行bgrewriteaof的时候,发现当前redis正在生成rdb文件的快照,会怎么样呢?
此时,aof重写操作就会等待,等待rdb快照生成完毕之后,再进行执行aof重写
子进程已经在重写了,父进程还需要继续写旧AOF文件吗?
需要,因为考虑到极端的情况,如果子进程重写的时候写到一半,此时服务器挂了,且父进程没有写旧的AOF文件,那么此时数据就会发生严重丢失。
自动触发
⾃动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时机。
auto-aof-rewrite-min-size:表⽰触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。
auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。
这和RDB自动触发差不多,就是要满足条件,满足了那就触发,不满足就不触发。
三、混合持久化
在AOF重写中,我们知道AOF是按照文本的方式来写入文件的,这样的方式成本是比较高的,所以redis就引入了混合持久化的方式。
该方式结合了rdb和aof的特点。
配置文件中默认是打开的:
过程:
刚开始按照aof的方式把每一个请求和操作都记入文件,等到触发aof重写的时,就会把当前内存中的数据状态按照rdb的二进制格式写入新的aof文件中,后续再进行的操作仍然时按照aof文本的方式追加到文件后面的。
如:
刚开始创建两条记录:
此时查看appendonly.aof文件:
然后执行bgrewriteaof命令,进行重写:
再次查看appendonly.aof文件:
可以发现此时aof文件已经是二进制的了。
四、启动redis时数据恢复的策略
当redis上同时存在aof文件和rdb快照的时候,以aof文件为主: