Redis持久化机制
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据恢复到工作状态的工作机制称为持久化
为什么要做持久化 ?为了防止防止服务重启、宕机时内存数据丢失,确保数据的安全性
-
RDB ( Redis DataBase ) 把数据以快照的形式完全备份在硬盘中,关注数据本身
-
AOF( Append Only File ) 把数据保存到硬盘中,关注操作过程
RDB
是Redis DataBase缩写快照,是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
RDB触发机制
-
save命令(同步):客户端向Redis发送save命令来创建一个快照文件。执行save命令的时候,如果存在老的快照文件,新的将会替换老的。
tip: 如果数据量太大,同步数据会执行很久,这期间 Redis 服务器也无法接收其他请求,导致不可用。(不建议使用)
-
bgsave命令(异步):客户端向Redis发送bgsave命令,Redis调用fork创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。
在没有CopyOnWrite之前,我们要给子进程生成虚拟空间,并为虚拟空间地每一个部分分配对应地物理空间,接着要把父进程对应部分地物理空间地内容复制到子进程的空间中。这实际上是个既耗时又耗费空间地操作。
有了COW之后, fork子进程时,我们只为其生成虚拟空间,但是并不先为每个部分分配真实的物理空间,而是让每个虚拟空间部分仍然指向父进程的物理空间。只有当父进程或子进程修改相应的共享内存空间时,才会为子进程分配物理空间并把父进程的物理空间内容进行复制。这就是所谓的写时复制,即把内存的复制延迟到了内存写入的时刻。
同时需要注意地是,父子进程共享的空间粒度是页( 在Linux中,页的大小为4KB ), 父/子进程修改某个页时,该页的共享才结束,同时子进程分配该页大小的物理空间复制父进程对应页的内容。这样,如果当子进程运行期间,父子进程都没有修改数据,那么操作系统就节省了大量的内存复制时间和占用空间。
- 通过 fork 创建的子进程能够获得和父进程完全相同的内存空间,父进程对内存的修改对于子进程是不可见的,两者不会相互影响;
- 通过 fork 创建子进程时不会立刻触发大量内存的拷贝,内存在被修改时会以页为单位进行拷贝,这也就避免了大量拷贝内存而带来的性能问题;
-
服务器配置定期自动触发
在 redis.conf 中配置: save 多少秒内数据变了多少 通过配置文件触发持久化的方式与 bgsave 命令类似,达到触发条件时会 fork 一个子进程进行数据保存
tip:线上的 Redis 环境不建议使用此方式。因为如果设置触发的时间太短,则容易频繁写入 rdb 文件,影响服务器性能,时间设置太长则会造成数据丢失。
为什么Redis的RDB备份不用多线程实现CopyOnWrite?
进程级的 COW触发时机是在 父进程/子进程对某个物理页的写入,这是由OS控制的;线程级的COW触发时机是在线程对共享数据写入,这里的数据是逻辑上的,需要开发者介入人为控制,比如上锁。这里拿 Java CopyOnWriteList 举例子不太恰当,而如果通过开发者自我实现,将被修改数据之前的副本拷贝到其他内存空间,这部分SNAPSHOT便得以保留,这样一来其实无论线程还是进程,都可以实现dump 出某个时间节点的内存快照的效果,只不过线程实现更麻烦而已
AOF
AOF(Append Only File)持久化,则是会记录客户端对服务器的每一次写操作命令,并将这些写操作以 Redis 协议追加保存到以后缀为 aof 文件末尾,在 Redis 服务器重启时,会加载并运行 aof 文件的命令,以达到恢复数据的目的。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
Redis 默认不开启 AOF 持久化方式,我们可以在 redis.conf 配置文件中开启并进行更加详细的配置
AOF三种策略对比
AOF重写机制(Redis 中的 AOF 文件太大了怎么办?)
因为 AOF 持久化是通过保存被执行的写命令来记录数据库状态的,随着 AOF 文件内容越来越多,文件的体积也越来越大。如果不对 AOF 文件加以管控的话,可能会对 Redis 服务器产生影响
重写后
Redis 服务器可以创建一个新的 AOF 文件来代替旧的 AOF 文件,重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合 Redis 会fork出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件。最后替换旧的aof文件。
Redis 不希望 AOF 重写造成服务器无法处理请求, 所以 Redis 决定将 AOF 重写程序放到后台子进程里执行, 这样处理的最大好处是:
-
子进程进行 AOF 重写期间,主进程可以继续处理命令请求。
-
子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。
注意:由于是模拟快照的过程,因此在重写AOF文件时并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件
AOF触发机制
- 在 redis.conf 中配置
-
执行 bgrewriteaof 命令
RDB-AOF混合持久化
redis4.0之后才支持,默认开启
- 优点:混合持久化结合了RDB持久化 和 AOF 持久化的优点,采取了rdb的文件小易于灾难恢复,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
- 缺点:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,需要专业的工具来阅读,因为是二进制,所以阅读性较差。
原理
混合持久化也是通过bgrewriteaof完成的,所以基本流程和上述一样。不同的是当开启混合模式时,fork出的子进程先将共享的内存副本全量以RDB的方式写入aof。这样提高了速度也极大的缩小了aof文件(毕竟都是二进制)。写完还是通知主进程,然后再将重写缓冲区的内容以AOF方式写入到文件,然后替换旧的aof文件。也就是说这种模式下的aof文件发生rewrite后前半部分是rdb格式(REDIS开头的二进制数据),后半部分是正常的aof追加的命令(重写缓冲区里的)。
如何选择合适的持久化方式
- 如果想达到数据足够的安全性,你应该同时使用两种持久化功能。
- 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
- 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
- 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。
持久化文件加载流程
当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢? 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
AOF和RDB文件都可以用于服务器重启时的数据恢复。AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件城后,Redis启动成功;AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。