Redis(Remote Dictionary Server)是一个键值对数据库服务器,服务器中通常包含任意个数据库,而每个非空数据库又可以包含任意个键值对。我们把服务器中的非空数据库以及它们的键值对,统称为数据库状态。
Redis是内存数据库,如果不将内存中的数据保存到磁盘中,一旦服务器进程退出,服务器中的数据库状态也会消失。为了解决这个问题,Redis提供了持久化功能。
从本篇文章开始总结Redis持久化相关的知识点,Redis持久化相关的内容可以分为RDB持久化、AOF持久化和混合机制三部分。
RDB持久化存储功能可以将某个时间点上的数据库状态保存到RDB文件中,即快照方式。RDB文件是一个经过压缩的二进制文件,通过该文件可以还原数据库状态。Redis默认的持久化方式就是RDB方式。
Redis会单独fork一个与当前进程一模一样的子进程来进行持久化,这个子进程的所有数据(环境变量,程序计数器等)都和原进程一模一样,会先将数据写入到一个临时rdb文件中,待持久化过程都结束了,再用这个临时rdb文件替换上次持久化好的rdb文件(dump.rdb),整个过程主进程不进行任何IO操作。
快照保存过程
- redis调用fork,现在有了子进程和父进程。
- 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时整个数据库的一个快照。
- 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出(fork一个进程,内存会是原来的两倍)。
有两个Redis命令可以生成RDB文件,SAVE
命令和BGSAVE
命令。由于BGSAVE
命令可以在不阻塞服务器进程的情况下执行,所以Redis允许用户通过设置服务器配置的save
选项,让服务器每隔一段时间自动执行BGSAVE
命令。例如如下配置,只有其中任意一个条件满足,服务器就会执行BGSAVE
命令。
save 900 1 # 服务器在900秒之内,对数据库进行了至少1次修改
save 300 10 # 服务器在300秒之内,对数据库进行了至少10次修改
save 60 10000 # 服务器在60秒之内,对数据库进行了至少10000次修改
有四种情况会触发RDB持久化,分别是
SAVE
命令、BGSAVE
命令、FLUSHALL
命令和退出Redis时。
SAVE命令
SAVE
命令会阻塞Redis服务器进程,直到RDB文件创建完成为止。在服务器进程阻塞期间,服务器不能处理任何命令请求。
BGSAVE命令
BGSAVE
命令会派生出一个子进程,子进程负责创建RDB文件,父进程继续处理命令。
RDB文件载入工作在服务器启动时自动执行,Redis并没有提供用于载入RDB文件的命令,只要Redis服务器启动时检测到RDB文件存在,就会自动载入RDB文件。
rdb文件的缺点是,最后一次持久化后的数据可能会丢失。
一个完整的RDB文件包括5个部分,分别是REDIS、db_version、databases、EOF、check_sum。其中,REDIS和EOF为常量,db_version、databases和check_sum三个部分为变量。
![]() |
---|
RDB文件结构 |
RDB文件中,各个部分的内容:
REDIS:
文件最开头的部分,5个字符占用5Byte,用以在程序载入文件时,检查所载入的文件是否RDB文件。
db_version:
字符串表示的整数,长度为4Byte,该整数记录了RDB文件版本号。
databases:
数据库状态(零个或多个数据库,以及各个数据库中的键值对数据)。
EOF:
RDB文件正文内容的结束标志,长度1Byte。当读入程序遇到该值时,标志数据库状态已经载入完毕。
check_sum:
校验和,长度为8Byte的无符号整数,这部分是程序通过对REDIS、db_version、databases、EOF四个部分的内容进行计算后得出。服务器载入RDB文件时,会将载入数据所计算出的校验和与check_sum对比,以此来检查RDB文件是否出错或损坏。
在RDB文件中,databases部分可以保存任意多个非空数据库。每个非空数据库在RDB文件中都可以保存为SECETCDB、db_number、key_value_pairs三部分。key_value_pairs是数据库存储数据的核心,涉及STRING、LIST、HASH、SET、ZSET、INTSET、ZIPLIST等其中类型的value编码。
![]() |
---|
RDB文件数据库结构示例 |
key_value_pairs部分:
- RDB文件中,key_value_pairs保存键值对和过期时间,不带过期时间的键值对由TYPE、key、value三部分组成。每个TYPE常量对应一种对象类型或者底层编码,key总是一个字符串对象,value根据TYPE类型的不同而有所不同。
- 带有过期时间的键值对在RDB文件中,包含EXPIRETIME_MS、ms、TYPE、key、value五个部分。EXPIRETIME_MS表示要读入的过期时间是以毫秒为单位,ms记录一个以毫秒为单位的UNIX时间戳,也就是键值对的过期时间。
AOF持久化是通过保存Redis服务器所执行的写命令,来记录数据库状态。
AOF持久化功能分为三个步骤,分别是命令追加,文件写入和文件同步。
当AOF持久化功能开启时,服务器执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓存区的末尾。
Redis服务器进程是一个事件循环,循环中的文件事件负责接收客户端命令请求,以及发送回复。服务器执行写命令时,会将命令加入到aof_buffer缓冲区。因此,服务器在每次结束一个时间循环以前,会调用flushAppendOnlyFile函数,决定是否将aof_buf缓冲区中的内容保存到AOF文件,而该函数的参数是通过配置文件appendfsync选项手动配置的。
appendfsync
选项的值直接决定AOF持久化功能的效率和安全,包括三个选项值:always
、everysec
和no
。
always:
每个事件循环都会将aof_buf缓冲区中的内容写入到AOF文件,并且同步。所以最慢,但最安全。
everysec:
每个事件循环都会将aof_buf缓冲区中的内容写入到AOF文件,每隔1秒在子线程对AOF进行一次同步。所以速度快,但相对来说不安全。
no:
每个事件循环都会将aof_buf缓冲区中的内容写入到AOF文件,至于何时同步,由操作系统控制。处于no模式下的flushAppendOnlyFile调用无需执行同步操作。所以写入速度最快,但不安全。
Redis服务器在启动时,只要读入并执行AOF文件中保存的写命令,就可还原服务器关闭前的数据库状态。
值得注意的是,由于AOF文件更新频率通常比RDB文件更新频率高,因此如果服务器开启了AOF持久化功能,服务器会优先使用AOF文件来还原数据库状态,也就是说只有在AOF持久化功能关闭时,服务器才会使用RDB文件来还原数据库状态。
AOF持久化功能下,Redis还原数据库状态步骤:
- 创建一个不带网络连接的伪客户端,用以执行AOF文件中的命令。
- 从AOF文件中解析并读取一条写命令
- 伪客户端执行被读入的写命令。
- 重复2、3两个步骤。
有一个问题是.aof文件会变得越来越大,为了解决aof文件体积膨胀问题,redis提供了AOF重写机制BGREWRTEOF
,将内存中数据库状态用命令的方式重写一个新的.aof文件。
Redis服务器是单线程来处理命令请求的,如果由服务器直接调用aof_rewrite函数,服务器重写AOF文件时会被阻塞。因此,Redis会创建一个子进程来执行AOF重写程序。当开启一个子进程,进行AOF文件重写时,如果此时Redis服务器又收到了写操作,会造成AOF文件和数据库状态不一致的情况。
为解决数据不一致问题,Redis设置了AOF重写缓冲区。在服务器创建子进程后使用,Redis执行完一个写命令后,会同时将写命令发送到AOF缓冲区和AOF重写缓冲区。当子进程完成AOF重写工作后,会向父进程发送一个信号,父进程收到信号后调用处理函数:(1)将AOF重写缓冲区中的内容写入新的AOF文件中。(2)为新的AOF文件改名,原子地覆盖现有AOF文件。整个AOF后台重写的过程中,只有信号处理函数执行时,会阻塞服务器进程。
auto-aof-rewrite-precentage 100
:当AOF文件大小增长率超过原大小的100%。
auto-aof-rewrite-min-size 64MB
:当AOF文件大小大于该配置项时字段开启。
假设重写完后时40MB,下次再重写就要根据上面的配置,到80MB时重写
RDB持久化方式优点在于:数据库还原速度。缺点有两个方面:持久化过程是将数据库状态全量的写入新的rdb文件,因此速度较慢。(2)如果服务器发生宕机,会丢失最后一次持久化之后的内存数据。
AOF持久化方式优点有两个方面:(1)持久化过程是将写命令增量的追加到.aof文件,因此速度较快。(2)如果服务器发生宕机,丢失的数据可以通过appendfsync
手动设置。缺点在于:数据库还原时,需要把.aof文件中的写命令执行以便,所以速度较慢。
4.0版本的混合持久化默认关闭,通过aof-use-rdb-preamble
配置参数控制,yes表示开启,no表示禁用,5.0之后默认开启。
混合持久化开启之后,fork出的子进程先将共享的内存副本全量的以rdb方式写入aof文件,然后再将重写缓冲区的增量命令以aof方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有rdb格式和aof格式的aof文件替换旧的aof文件。简单来说:新的aof文件前半段是rdb格式的全量数据后半段是aof格式的增量数据。
混合持久化是通过bgrewriteaof
完成的,
优势:
劣势:
[1] 黄健宏.Redis设计与实现[M].北京:机械工业出版社