0%

redis03_持久化

redis持久化

概述

redis数据都在内存里面,为防止数据丢失,redis提供持久化功能。持久化方式如下

  • RDB持久化
  • AOF持久化
  • 混合持久化(4.0之后新增)

RDB持久化

RDB持久化功能所生成的RDB文件时一个经过压缩的二进制文件,该文件可还原生成RDB文件时的数据库状态。

RDB文件的生成和加载

SAVE和BGSAVE命令可以生成RDB文件。

  • SAVE命令会阻塞redis服务器进程,直到RDB文件创建完毕为止,阻塞期间,服务器不能处理任何请求。
  • BGSAVE命令会派生出一个子进程,由子进程负责创建RDB文件,服务器进程继续处理请求。

在BGSAVE命令执行期间,服务器对SAVE、BGSAVE、BGWRITEAOF三个命令的处理方式会不同

  • SAVE命令会被直接拒绝,redis禁止SAVE和BGSAVE同时调用,为了避免父子进程同时调用rdbSave,防止产生竞争条件。
  • BGSAVE会被拒绝,同时两个BGSAVE命令也会产生竞争条件。
  • BGWRITEAOF会被延迟到BGSAVE命令执行之后。(如果有BGWRITEAOF命令正再执行,BGSAVE请求会被服务器拒绝。)

BGWRITEAOF和BGSAVE之间没有冲突的地方,出于性能考虑,如果两个子进程同时执行大量磁盘写入操作,会造成磁盘IO过高。

加载

服务器启动时检测到RDB文件的存在,会自动载入RDB文件,载入期间会一直阻塞,直到载入工作完成。

AOF更新频率更高,如果服务器开启了AOF功能,会优先使用AOF文件还原数据库。

自动间隔保存

Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。

满足下面条件中的任意一个,BGSAVE会自动执行。

1
2
3
4
5
6
7
8
9
//900秒内对数据库进行至少一次修改
save 900 1
//300秒内对数据库进行至少10次修改
save 300 10
//60秒内对数据库进行至少1000次修改
save 60 1000

// 关闭自动保存功能
//save ""

dirty和lastsave

服务器维持着一个dirty计数器和一个lastsave属性

  • dirty计数器,记录上次成功执行SAVE或BGSAVE命令之后,服务器对数据库进行了多少次修改。
  • lastsave属性是一个UNIX时间戳,记录了服务器上次成功执行SAVE或BGSAVE命令的时间。

服务器定期检查保存条件是否满足
Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,它其中的一项工作就是检查save选项所设置的保存条件是否满足,满足则只需BGSAVE命令。

AOF持久化

AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。

AOF持久化的实现

AOF持久化功能的实现可以分为命令追加、文件写入、文件同步三个步骤。

命令追加

服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区末尾。

AOF文件的写入与同步

操作系统的文件写入和同步:为了提高文件的写入效率,现代操作系统中,当用户调用操作系统的write函数的时候,数据会被写到一个内存缓冲区里面,当缓冲区填满或者超过指定时限后,才会从缓冲区写到磁盘里面。同时系统提供同步函数,强制让操作系统将缓冲区的数据写到硬盘里面

Redis的服务器进程是一个事件循环,每次结束一个事件循环之前,都会调用flushAppenOnlyFile函数,考虑是否将aof_buf中的内容写入和保存AOF文件中。

AOF文件的载入与数据还原

AOF重写

随着服务器运行时间越长,AOF文件中的内容会越来越多,文件体积膨胀,并且会导致使用AOF文件进行数据还原所需要的时间边长。

为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写功能。

AOF文件重写的实现

AOF文件重写并不需要对现有的AOF文件进行任何修改,这个功能通过读取服务器当前的数据库状态来实现的。

程序从数据库中读取键现在的值,然后用一个命令去记录键值对,代替之前记录这个键值对的多条命令,这就是AOF重写功能的实现原理(如list对象,分多次加入对象,会有多条命令)。

重写程序在处理列表、哈希表、集合、有序集合时,会先检查键所包含的元素,如果元素的数量超过了REDIS_AOF_REWRITE_ITEMS_PER_CMD常量(目前版本为64)时,重写程序会用多条命令来记录这个集合。

AOF后台重写

AOF重写程序对进行大量的写入操作,所以调用这个函数的线程将被长时间阻塞,为了使重写AOF期间,服务器正常相应请求,Redis决定将AOF重写程序放到子进程中执行。

为了保证AOF后台重写期间,收到的写入请求会被同步到AOF文件中,Redis设置了一个AOF重写缓冲区,这个在子进程创建之后开始使用,当Redis执行完一个写命令之后,会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。

当子进程完成AOF重写工作之后,他会给父进程发送一个信号,父进程接收到信号后会执行一下工作:

  1. 将AOF重写缓冲区中的所有内容写到新AOF文件中。
  2. 对新的AOF文件进行改名,原子地覆盖现有的AOF文件。

AOF混合持久化

当Redis实例很大时,使用AOF日志重放,启动数据库需要花费很长时间。

为了解决这个问题Redis4.0提供了新的持久化选项 混合持久化

混合持久化将RDB文件的内容和增量的AOF日志文件放在一起,AOF日志不是全量日志,是从持久化开始到持久化结束这段时间发生的增量AOF日志

BGSAVE原理(fork 和写时复制技术(COW))

使用BGSAVE,Redis会调用glibc函数fork产生一个子进程,子进程刚产生时,他和父进程共享内存中相同的代码和数据段。

子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。

这个时候就会使用操作系统的COW机制来进行数据段页面的分离。
数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的, 还是进程产生时那一瞬间的数据。