文章目录
为了管理Redis数据,Redis进程除了会基于内存进行数据存储之外,也会将所存储的数据持久化到磁盘之中。
Redis数据持久化流程
具体步骤如下:
- Redis客户端通过程序或命令向服务端发送数据写入操作。此时,数据保存在客户端内存中。
- Redis服务器接收到用户数据写入请求,并将数据保留在Redis服务端进程的内存之中。
- 服务端需要将内存中保存的数据写入磁盘。此时Redis进程需要调用系统的写入进程,随后将要写入的数据写入系统内存缓冲区之中。
- 操作系统将系统内存缓冲区中的数据写入磁盘缓冲区。
- 操作系统将磁盘缓冲区中的数据写入磁盘的物理介质之中,实现数据持久化存储。
RDB持久化机制
RDB(Redis DataBase,Redis数据库)是Redis数据库默认的数据持久化机制,其核心特点是将数据以快照的形式保存在磁盘上,在每一次进行数据写入时,都会fork一个新的子进程,将当前数据以二进制的形式写入dump.rdb文件中。由于这种方式写入的数据较为完整,因此称为全量备份模式。
RDB会丢失部分新数据。
当数据持久化子进程创建完成之后,实际上此时的主进程依然可以对外提供数据读写的支持,但是此时的数据只在主进程之中保存。因此子进程进行数据写入时,不会写入这些新数据,这就导致了子进程写入的数据可能是不完整的。
1. RDB触发机制
RDB处理的核心在于某一个时刻将所有的数据生成一个快照来进行最终的备份,所以需要有一种触发机制来实现这一过程。Redis提供三种触发方式:SAVE命令手动触发、BGSAVE命令手动触发以及自动触发。
1.1 SAVE命令手动触发
该命令执行时会启用数据持久化机制。为了获得完整的持久化数据,该命令会阻塞当前的Redis进程,此时其他的客户端进程无法进行数据处理,直到RDB处理完成。这种方式会导致大范围的客户端操作延迟,所以在开发中一般不会轻易使用。
1.2 BGSAVE命令手动触发
管理员发出该命令之后,Redis会在后台启动一个异步的子进程,并且在异步子进程之中形成一个完整的数据快照,由异步子进程负责将数据写入,此时Redis可以正常对外提供数据服务。这种方式只会在子进程创建阶段短暂地出现阻塞,随后子进程在数据写入完成后自动结束,同时由于子进程会提供与主进程相同的数据项,因此会产生额外的内存开销。
1.3 自动触发
这是在Redis进程启动时定义的,其会自动根据指定的时间间隔以及数据变化的数量自动执行BGSAVE持久化命令。
自动触发需要通过redis.conf配置文件进行定义。其中save m n
的配置项表示自动持久化的触发机制,m表示时间间隔,n表示存在变化项的个数。例如每10秒有2000个变化项,可以配置save 10 2000
,达到该量级时会自动执行BGSAVE命令。
默认配置:
save 3600 1 300 100 60 10000
配置含义是如果3600秒发生至少1次写入、300秒发生至少100次写入或60秒发生至少10000次写入,自动触发RDB备份。
序号 | 配置项 | 默认值 | 描述 |
---|---|---|---|
1 | save SECONDS CHANGES [ SECONDS CHANGES … ] | 设置自动触发持久化,不设置表示停用 | |
2 | stop-writes-on-bgsave-error | yes | 当启用RDB且最后一次保存数据失败时,Redis停止接收数据,这样可以在灾难发生时,引起使用人员的注意 |
3 | rdbcompression | yes | 是否对存储到磁盘中的快照进行压缩 |
4 | rdbchecksum | yes | 使用CRC64算法来进行数据校验,开启此功能后会增加10%的性能损耗 |
5 | dbfilename | dump.rdb | 设置快照文件的存储名称 |
6 | dir | ./ | 快照文件的存放路径,该配置项需要设置目录 |
2. RDB协议
2.1 字符串的保存格式
Redis的RDB文件是内存数据库的二进制表示。Redis可以加载二进制文件内存。不管是文本协议,还是二进制协议,都是有格式的。
RDB中字符串的保存格式是LENGTH + STRING。其中,STRING是具体的字符串内容,LENGTH表示字符串的长度,它是变长字段,通过首字节能够知道LENGTH字段有多长,然后读取LENGTH字段可以知道具体的STRING长度。
00xxxxxx
字符串长度<64
01xxxxxx xxxxxxxx
字符串长度<16384
10000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
字符串长度<UINT32_MAX
10000001 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
字符串长度<UINT64_MAX
AOF持久化机制
使用RDB方式进行的数据持久化处理,可以实现数据的快速恢复,但是在每次执行RDB持久化操作时,都需要启动新的子进程,并且会产生额外的内存开销。为进一步提高缓存数据持久化的效率,Redis提供了AOF(Append Only File,只追回文件)持久化机制。该机制记录的并不是简单的数据,而是用户所执行的每一条数据操作命令。Redis使用RESP 3协议编码命令。
如果AOF机制只是将用户执行的命令记录到AOF数据文件中,那么在高并发访问时就有可能产生大量的备份命令。这不仅造成AOF数据文件激增,也会影响数据恢复的速度。因此,Redis采用命令重写的处理机制以实现命令压缩处理。
例如,现在有3个不同的Redis客户端同时执行LPUSH命令,并且向同一个List集合存储数据1、2、3。考虑到命令优化的处理性能,Redis并不是针对用户执行的命令进行合并,而是对最终数据库中所存储的数据内容进行分析,而后得出最终的执行命令,这一过程为命令压缩处理。
另外,Redis在执行AOF机制时,为了保证Redis服务正常运行,采用子进程的方式来进行处理。但是这又带来数据一致性问题,即在子进程创建完成后,又有大量的客户端通过主进程进行数据的修改。此时子进程中持久化的命令无法侦测到这些变化。因此,Redis采用了AOF缓冲区的机制。一个是主进程创建的AOF缓冲区,一个是子进程创建的AOF重写缓冲区。在数据持久化时,用户发来的数据更新命令会同时保存在两个缓冲区中。前者将命令以非压缩的形式写入AOF数据文件中;后者会对命令进行压缩(操作较耗时),并且将压缩后的内容写入AOF数据文件中,最后使用压缩的AOF数据文件替换原始的AOF数据文件。
由于每一次持久化处理的最后,子进程都要使用重写后的AOF数据文件替换主进程生成的AOF数据文件,这样会造成内存、CPU和磁盘I/O的消耗。在Redis 7.x之后的版本采用了多AOF数据文件存储设计:
- BASE文件:表示基础AOF数据文件,一般由子进程重写产生,最多只有一个。
- INCR文件:表示增量AOF数据文件,一般会在AOF持久化开始执行时创建,该类型的文件可能有多个。
- HISTORY文件:表示历史AOF数据文件,每一次AOF完成后都会将之前的文件转化为历史文件,并自动删除。
appendfsync配置
AOF持久化最终需要将缓冲区中的内容写入文件。写文件操作通过操作系统提供的write函数执行。然后,写操作之后,数据只是保存在内核的缓冲区中,真正写入磁盘还需要调用fsync函数。fsync函数是一个阻塞并且执行速度比较慢的函数,所以Redis通过appendfsync配置指令控制fsync函数的频次,通常有三种模式:
- no:不执行fsync函数,由操作系统负责数据的刷盘;数据安全性低,但Redis性能最高。
- always:每执行一次写入操作就会执行一次fsync函数;数据安全性最高,但会导致Redis性能下降。
- everysec:每秒执行一次fsync函数;在数据安全性和Redis性能之间达到一个平衡。生产环境一般也会这样配置,即每秒执行一次。
混合持久化
混合持久化是进行AOF重写时,子进程将当前时间点的数据快照保存为RDB文件格式,而后将父进程累积命令保存为AOF格式。
加载时,Redis首先会识别AOF文件是否以REDIS字符串开头,如果是,就按RDB格式加载,加载完RDB后继续按AOF格式加载剩余部分。
通过以下命令设置是否开启混合持久化功能:
aof-use-rdb-preamble yes