Redis持久化
Redis持久化是指将数据写入磁盘,Redis本身提供了一系列持久化选项:
RDB(Redis Database):RDB持久化是以指定的时间间隔,执行数据集的时间点快照;
AOF(Append Only File):AOF持久化以日志追加的方式记录服务器接收到的每个写操作;
Redis混合持久化:在redis服务中同时开启RDB持久化和AOF持久化;
Redis无持久化:如果希望数据只要服务器正在运行,可以完全禁用持久性;
一 RDB
RDB是Redis Database的缩写,是Redis默认的持久化方式,在指定的时间间隔,将某一个时刻内存中的数据和状态以文件的形式写到磁盘上,也就是将内存中的数据集以snapshot内存快照的方式写入磁盘文件;
Redis的数据都在内存中,保存备份时执行的是全量快照,把内存中所有的数据都记录到磁盘中,然后恢复时再将硬盘快照文件直接读回到内存里;
Redis数据保存在磁盘,即使服务器故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证;
RDB持久化保存到磁盘的文件,默认文件名称是dump.rdb,快照文件称为RDB文件;
1.1 RDB持久化触发方式
RDB持久化触发的方式有自动触发和手动触发两种方式;
1.1.1 自动触发
redis.conf配置文件中的RDB持久化相关属性配置:
save m n:在指定的时间间隔m秒内数据集有n次修改,或是超过指定的时间间隔m秒后有n次数修改,都会自动触发bgsave命令;
dir /mangoRedis:设置rdb文件保存路径;
dbfilename dumpMangoOne.rdb:设置rdb文件名称;
stop-writes-on-bgsave-error yes:属性默认值是yes,如果配置成no,那么在bgsave快照写入失败时,redis也能继续接受新的写请求;
rdbcompression yes:默认值yes,redis采用LZF算法对存储到磁盘中的快照文件进行压缩存储,如果不想消耗CPU来进行压缩的话,可以设置为no关闭此功能;
rdbchecksum yes:默认值是yes,用来对快照文件使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以设置为no关闭此功能;
rdb-del-sync-files no:默认值是no,主从进行全量同步时,通过传输RDB内存快照文件实现,没有开启RDB持久化的实例在同步完成后会删除该文件,通常情况下保持默认即可;
127.0.0.1:6379> config get save
1) "save"
2) "5 2"
127.0.0.1:6379> config get dir
1) "dir"
2) "/mangoRedis"
127.0.0.1:6379> config get dbfilename
1) "dbfilename"
2) "dumpMangoOne.rdb"
127.0.0.1:6379>
在redis.conf配置文件中的SNAPSHOTTING下配置save参数,来触发redis的RDB持久化条件;
redis7之前的配置情况:
save 900 1:在900秒内数据集有1次修改,自动触发bgsave命令,就写一份新的RDB文件;
save 300 10:在300秒内数据集有10次修改,自动触发bgsave命令,就写一份新的RDB文件;
save 60 10000:在60秒内数据集有10000次修改,自动触发bgsave命令,就写一份新的RDB文件;
redis7的配置情况:
save 3600 1:在900秒内数据集有1次修改,自动触发bgsave命令,就写一份新的RDB文件;
save 300 10:在300秒内数据集有100次修改,自动触发bgsave命令,就写一份新的RDB文件;
save 60 10000:在60秒内数据集有10000次修改,自动触发bgsave命令,就写一份新的RDB文件;
1.1.2 手动触发
redis提供了两个命令来手动触发rdb文件备份,save命令和bgsave命令;
在主程序中执行save命令,会阻塞主进程,当前redis服务器不能处理其他命令,直到持久化工作完成,生产环境禁止使用save命令;
在主程序中执行bgsave命令时,redis会fork一个子进程,这个子进程会读取内存中的数据集,并将其写入到一个临时的rdb文件中,当写入完成后,这个临时的rdb文件会替换掉旧的rdb文件,从而实现数据的更新,在这个过程中,不会阻塞主进程,bgsave命令在后台异步执行快照操作,主进程可以继续处理客户端的请求,不会对redis的性能造成太大的影响;
在Linux程序中,fork函数是指产生一个和父进程完全相同的子进程,但子进程在此后都会exec系统调用,出于效率考虑,尽量避免膨胀;
两种命令的优缺点及使用场景:
save命令的优点,操作简单,并且生成的rdb文件较小;
save命令的缺点,会阻塞主进程,redis服务不能处理任何请求,影响性能;
bgsave命令的优点,不会阻塞主进程,不影响redis服务正常运行,在大数据集的情况下相对save更有优势;
bgsave命令的缺点,生成的rdb文件较大,子进程会占用一定的系统资源;
对于数据量小的服务,可以使用save命令触发,不会影响正常服务;
对于数据量大的服务,可以使用bgsave命令触发,后台异步执行,不会阻塞主线程;
tips:
执行flashdb命令,和flashall命令也会产生rdb文件,但是文件是空的,没有意义;
执行shutdown命令,且没有设置开启AOF持久化,触发rdb持久化;
主从复制时,主节点自动触发rdb持久化;
在redis客户端命令行执行lastsave命令,获取最后一次成功执行快照的时间戳;
1.2 RDB持久化的步骤
1.2.1 rdb持久化的步骤
触发持久化:手动触发执行bgsave命令或自动触发(满足redis.conf配置文件中的save条件)来启动rdb持久化;
创建子进程:主进程检查是否存在正在运行的子进程,若不存在redis主进程会调用fork函数创建子进程,这个子进程会继承主进程的内存数据;
写临时文件:子进程将内存数据写入到一个临时的rdb文件(temp-<pid>.rdb)中,在子进程写入过程中,主进程通过写时复制(COW)机制处理新写入的数据,确保子进程数据一致性;
替换旧文件:子进程写临时文件的过程是异步的,当临时文件写入完成后,redis会用临时文件原子替换掉旧的rdb文件,从而实现数据的更新;
完成通知,子进程完成RDB文件生成后通知主进程,主进程将临时文件重命名为正式RDB文件替换旧版本 ,主进程更新持久化统计信息;
1.2.2 Linux的Copy-On-Write写时复制机制
Redis的RDB持久化机制中,主进程的写时复制(Copy-On-Write,COW)机制是保证数据一致性,服务高可用和实现高效内存快照的核心技术,具体流程如下:
fork子进程阶段,主进程执行bgsave命令时,调用fork()创建子进程,此时父进程和子进程共享物理内存空间(内存页),内存页被标记为只读状态,这个过程仅复制父进程的页表(Page Table),而非实际数据,因此fork操作耗时极短(微秒级);
主进程写操作触发复制,主进程继续处理客户端请求时,若接受数据写操作指令时,共享内存页会因写权限限制触发页错误中断(Page Fault),内核会将被修改的页复制为独立副本(新物理地址),主进程将新数据写入复制后的内存页 ,子进程仍读取原始页数据,保证RDB文件内容为fork时刻的数据快照,即是原内存页A被修改后,系统生成新页A'供主进程使用,子进程继续读取未修改的页A;
子进程持久化,子进程基于fork时的内存状态生成RDB文件,期间仅读取原始内存页,由于COW机制,子进程数据视图在快照生成期间保持静态,确保RDB文件的一致性;
子进程生成RDB期间的内存管理:
主进程通过fork()创建子进程时,子进程共享主进程的物理内存页,此时仅复制页表(虚拟内存映射关系),而非实际内存数据;
物理内存会被标记为只读(read-only),若主进程在此期间尝试写入数据,会触发操作系统的写时复制(Copy-On-Write, COW)机制,主进程将待修改的原始内存页复制一份副本,并在副本上完成写入操作,子进程仍读取原始内存页的数据生成RDB文件,不受主进程写入操作影响;
当子进程完成RDB文件生成后,主进程对复制的内存页的处理机制如下:
无需主动处理复制的内存页,主进程在子进程运行期间产生的副本内存页会被保留为当前数据集的一部分,主进程继续使用这些副本处理后续请求;
旧内存页的回收,未发生修改的原始内存页(未被COW复制的部分)可能被操作系统自动回收,或随主进程后续操作逐步替换,但这一过程由操作系统管理,无需主进程干预;
主进程在子进程生成RDB完成后,继续使用COW机制产生的副本内存页处理数据操作,原始内存页的释放由操作系统自动完成,这一机制确保了RDB持久化期间主进程的服务可用性,同时避免显式内存管理带来的性能损耗;
Copy-On-Write写时复制机制通过操作系统级内存页管理实现,Redis自身仅需调用fork()和监听子进程状态,COW的优势:
内存高效利用,避免全量内存拷贝,初始阶段父子进程共享内存页,物理内存消耗仅增加约2MB页表开销,仅在实际发生写操作时复制内存页,未修改的页保持共享,减少内存冗余占用;
低延迟处理,主进程仅在fork时有短暂阻塞(微秒级),后续写操作仅复制被修改页,对服务性能影响极小;
数据一致性保证,子进程始终基于fork时间点的数据生成快照,不受后续写操作影响,确保RDB文件内容一致性 ;
COW的劣势:
频繁写入场景,大量写操作会导致内存页频繁复制,增加内存碎片和物理内存消耗 ,可能接近双倍内存占用;
内存碎片风险,长期运行的Redis实例若多次执行bgsave,可能因COW机制积累内存碎片,需定期重启优化;
1.3 数据恢复
在redis.conf配置文件中的dir属性和dbfilename属性,是对应rdb文件存储路径和文件名字,启动服务就可以根据redis.conf配置文件的设置,读取rdb文件到内存中;
执行flushdb或flushall命令时,会生成rdb文件,但是空的rdb文件,在恢复数据时,读取到内存中的数据是空的;
备份数据时,要将redis服务和rdb备份文件分机隔离,各自存储,不要把和生产redis服务器和rdb备份文件放在同一台机器,以防生产机物理损坏后备份文件也挂了;
1.4 检查修复RDB文件
redis的redis-check-rdb命令,用于检查修复rbd文件
[root@MiWiFi-RD04-srv bin]# ll
total 29244
drwxr-xr-x. 2 root root 25 Mar 2 20:29 mangoRedisConfig
-rwxr-xr-x. 1 root root 6899128 Feb 20 22:17 redis-benchmark
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-check-aof -> redis-server
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-check-rdb -> redis-server
-rwxr-xr-x. 1 root root 7619024 Feb 20 22:17 redis-cli
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 15418800 Feb 20 22:17 redis-server
drwxr-xr-x. 2 root root 25 Feb 23 01:16 zm
[root@MiWiFi-RD04-srv bin]# redis-check-rdb /mangoRedis/dumpMangoOne.rdb
[offset 0] Checking RDB file /mangoRedis/dumpMangoOne.rdb
[offset 26] AUX FIELD redis-ver = '7.2.6'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1740918753'
[offset 67] AUX FIELD used-mem = '1012616'
[offset 79] AUX FIELD aof-base = '0'
[offset 81] Selecting DB ID 0
[offset 121] Checksum OK
[offset 121] \o/ RDB looks OK! \o/
[info] 4 keys read
[info] 0 expires
[info] 0 already expired
[root@MiWiFi-RD04-srv bin]#
1.5 RDB持久化特点
1.5.1 RDB优势
RDB文件紧凑,RDB文件采用二进制快照机制,文件体积较小,节省磁盘空间,定期生成数据快照,避免实时记录所有操作,减少对系统性能的影响,这样可以在宕机重启时轻松恢复不同版本的数据集;
RDB文件恢复速度快,RDB文件是完整的数据集,重启时直接加载到内存,恢复效率远高于AOF,尤其在大数据集场景下;
RDB文件资源占用可控,通过bgsave命令触发持久化时,主进程仅需fork子进程处理数据快照,主线程继续处理请求,不会执行磁盘I/O或类似操作,最大限度地提高了Redis的性能;
RDB文件性能高,与AOF文件相比,RDB文件适用大数据集更快地重启,在内存中的加载速度要比AOF快得多,在副本上RDB文件支持重启和故障转移后的部分重新同步;
1.5.2 RDB劣势
数据丢失风险,在一定时间间隔做一次备份,如果redis服务宕机,就会丢失从当前到最近一次快照期间的数据,对数据一致性要求高的场景不友好;
内存压力大,内存数据的全量同步,fork子进程时,如果数据量太大,可能导致内存占用翻倍,极端情况下触发OOM,导致I/O严重影响服务器性能;
潜在阻塞风险,RDB持久化依赖于主进程的fork,在更大的数据集中,fork可能会很耗时,可能导致服务请求的瞬间延迟,可能会导致Redis停止为客户端服务几毫秒甚至一秒钟;
配置灵活性低,持久化依赖预设的触发条件,无法按需实时保存,需权衡数据安全性与性能;
1.6 禁用RDB快照
在redis.conf配置文件中设置save属性,save " ",禁用RDB持久化数据
二 AOF
2.1 AOF介绍
AOF(Append Only File),是以日志形式记录每一个写操作,将写操作追加到AOF文件中,然后在服务器启动时再次重放这些操作,重建原始数据集,命令的记录格式与redis协议本身相同。
redis.conf配置文件中的AOF持久化相关选项配置:
appendonly no:默认值no,redis没有开启AOF,需要设置属性为yes开启AOF持久化功能;
appendfilename "appendonly.aof":AOF保存写指令的文件名,默认是appendonly.aof文件;
appenddirname "appendonlydir":AOF保存appendonly.aof文件的父级目录;
appendfsync everysec:AOF的写回策略,默认是everysec,可选值是always、everysec和no;
no-appendfsync-on-rewrite no:AOF重写期间是否同步,默认值是no,AOF重写期间,主线程可以进行持久化数据,可以保持数据一致性,但是效率低,值为yes时,数据只是保存在内存中,数据可能会丢失;
auto-aof-rewrite-percentage 100:重写触发配置,AOF文件大小达到指定百分比增长,若上次重写后文件为1GB,当文件增长至2GB(即增长100%)时触发重写;
auto-aof-rewrite-min-size 64mb:重写触发配置,AOF文件大小满足最小值限制,默认是64mk;
aof-load-truncated yes:用于控制AOF(Append-Only File)持久化文件加载行为的配置参数,当 Redis启动时,若检测到AOF文件末尾存在不完整或损坏的指令(例如因意外宕机导致指令未完全写入),默认值yes会尝试加载文件中已正确写入的部分,并自动忽略最后一条可能异常的指令,适用于对数据丢失有一定容忍度的场景,优先保障服务可用性,若对数据完整性要求极高,可设为no,但需接受Redis可能因AOF文件问题无法启动的风险;
aof-use-rdb-preamble yes:Redis中用于启用混合持久化(RDB-AOF 混合模式)的配置参数,默认值yes启用,Redis在触发AOF重写时,会将当前内存数据的RDB快照作为AOF文件的前半部分(二进制格式),后续增量数据仍以AOF命令追加到文件末尾,譬如生成的appendonly.aof文件内容可能以REDIS开头的RDB格式存储快照,后接AOF格式的写命令,当Redis重启时,优先加载AOF文件中的RDB快照部分快速恢复数据,再重放后续的AOF命令补全增量数据,兼顾恢复速度和数据完整性;
aof-timestamp-enabled no:配置项用于控制AOF文件是否记录命令执行的时间戳,默认值no,AOF文件中不包含命令执行的时间戳信息,设置值为yes开启该选项后,AOF文件中将包含命令执行的时间戳信息,这有助于在调试时更准确地定位命令执行的时间点,但会增加AOF文件的大小和解析的复杂度;
AOF文件保存的路径:
redis7之前,AOF文件保存的位置和RDB文件保存的位置是一样的,都是通过redis.conf配置文件的dir属性设置文件保存路径;
redis7开始,AOF文件保存的位置是redis.conf配置文件的dir属性配置和appenddirname属性搭配起来的路径位置;
AOF文件保存的名称:
redis7之前,Redis有且只有一个appendonly.aof文件;
redis7开始,Redis使用Multi Part AOF(多部分AOF)机制,也就是说,原始的单个AOF文件被拆分为基本AOF文件(最多一个),增量AOF文件(可能有多个)和历史AOF文件三种类型:
base.rdb:表示基础AOF文件,一般由子进程重写产生,是重写AOF文件时存在的数据的初始(RDB或AOF格式)快照;
incr.aof:表示增量AOF文件,一般会在重写开始执行时被创建,增量文件包含自上次创建基本AOF文件以来的增量更改;
history:表示历史AOF文件,由base和incr变化而来,每次AOFRW成功完成时,本次重写之前对应的base和incr AOF文件都将变为history,history类型的AOF会被redis自动删除;
这些AOF文件放在dir+appenddirname文件目录中,并在文件目录中引入manifest清单文件来跟踪管理这些AOF文件;
在启动redis服务的时候,appenddirname目录会自动创建完成,并且会生成如下三个文件:
-rw-r--r--. 1 root root 88 Mar 2 20:43 appendonlyMangoOne.aof.1.base.rdb
-rw-r--r--. 1 root root 0 Mar 2 20:43 appendonlyMangoOne.aof.1.incr.aof
-rw-r--r--. 1 root root 104 Mar 2 20:43 appendonlyMangoOne.aof.manifest
案例演示:
redis.conf配置文件AOF属性配置如下:
1389 appendonly yes
1416 appendfilename "appendonlyMangoOne.aof"
1422 appenddirname "appendonlyMangodir"
初始化执行:
####redis服务刚跑起来时......
[root@MiWiFi-RD04-srv bin]# redis-server mangoRedisConfig/redis7.conf
[root@MiWiFi-RD04-srv bin]# redis-cli
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
##持久化文件情况......
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 0
drwxr-xr-x. 2 root root 127 Mar 2 20:43 appendonlyMangodir
[root@MiWiFi-RD04-srv mangoRedis]# ll appendonlyMangodir/
total 8
-rw-r--r--. 1 root root 88 Mar 2 20:43 appendonlyMangoOne.aof.1.base.rdb
-rw-r--r--. 1 root root 0 Mar 2 20:43 appendonlyMangoOne.aof.1.incr.aof
-rw-r--r--. 1 root root 104 Mar 2 20:43 appendonlyMangoOne.aof.manifest
[root@MiWiFi-RD04-srv mangoRedis]#
####往redis服务添加数据......
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
127.0.0.1:6379>
##持久化文件情况......
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 20:43 appendonlyMangodir
-rw-r--r--. 1 root root 121 Mar 2 20:51 dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# ll appendonlyMangodir/
total 12
-rw-r--r--. 1 root root 88 Mar 2 20:43 appendonlyMangoOne.aof.1.base.rdb
-rw-r--r--. 1 root root 101 Mar 2 20:51 appendonlyMangoOne.aof.1.incr.aof
-rw-r--r--. 1 root root 104 Mar 2 20:43 appendonlyMangoOne.aof.manifest
[root@MiWiFi-RD04-srv mangoRedis]#
恢复数据执行:
####redis命令执行
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
3) "k4"
4) "k3"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
##redis持久化文件
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 21:42 appendonlyMangodir
-rw-r--r--. 1 root root 121 Mar 2 21:46 dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# cp -r appendonlyMangodir/ appendonlyMangodirbak
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 21:42 appendonlyMangodir
drwxr-xr-x. 2 root root 127 Mar 2 21:46 appendonlyMangodirbak
-rw-r--r--. 1 root root 88 Mar 2 21:48 dumpMangoOne.rdb
####redis命令执行
127.0.0.1:6379> shutdown
not connected> quit
##redis持久化文件
[root@MiWiFi-RD04-srv mangoRedis]# rm -f dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 0
drwxr-xr-x. 2 root root 127 Mar 2 21:42 appendonlyMangodir
drwxr-xr-x. 2 root root 127 Mar 2 21:46 appendonlyMangodirbak
####redis命令执行, 看flushdb数据库之后的持久化文件
[root@MiWiFi-RD04-srv bin]# redis-server mangoRedisConfig/redis7.conf
[root@MiWiFi-RD04-srv bin]# redis-cli
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
####redis命令执行, 看flushdb数据库之前的持久化文件
##redis持久化文件
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 21:42 appendonlyMangodir
drwxr-xr-x. 2 root root 127 Mar 2 21:46 appendonlyMangodirbak
-rw-r--r--. 1 root root 88 Mar 2 21:48 dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# rm -rf appendonlyMangodir
[root@MiWiFi-RD04-srv mangoRedis]# rm -f dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# mv appendonlyMangodirbak/ appendonlyMangodir
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 0
drwxr-xr-x. 2 root root 127 Mar 2 21:46 appendonlyMangodir
####redis命令执行
[root@MiWiFi-RD04-srv bin]# redis-server mangoRedisConfig/redis7.conf
[root@MiWiFi-RD04-srv bin]# redis-cli
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k4"
4) "k1"
127.0.0.1:6379>
2.2 AOF持久化流程
命令写入缓冲区,所有新的写操作指令会首先被追加到内存中的AOF缓冲区(aof_buf)进行保存,而非直接写入磁盘,这样设计避免了频繁的磁盘I/O操作对性能的影响;
缓冲区同步策略,根据redis配置文件中appendfsync参数定义的同步策略,将缓冲区内容同步到磁盘的AOF文件;
文件重写阶段,随着AOF文件的增加,满足AOF文件重写触发条件时,会创建子进程,基于当前数据库状态生成新AOF文件(临时文件),替代旧文件中的冗余命令,重写期间,主进程继续处理命令,新命令同时追加到原aof_buf缓冲区和重写缓冲区,确保数据一致性;
2.2.1 AOF缓冲区同步策略
在Redis AOF持久化机制中,将AOF缓冲区的数据同步到磁盘中涉及到write和fsync两个系统调用:
write系统调用是指将用户空间缓冲区的数据写入内核空间缓冲区,非阻塞操作;
fsync系统调用是指将内核空间缓冲区的数据写入物理磁盘中,阻塞操作;
在Redis AOF持久化机制中,AOF缓冲区的数据同步到磁盘中的完整过程是:
Redis调用write系统调用将数据从用户空间缓冲区(AOF缓冲区)写入内核缓冲区后,会立即清空自己的用户空间缓冲区,等待接收新的命令数据;
根据AOF配置的同步策略,Redis会周期性调用fsync系统调用,强制内核将缓冲区数据刷盘,刷盘完成后,内核缓冲区的数据由操作系统自行管理,Redis不干预其删除逻辑;
AOF的appendfsync同步策略到磁盘的属性,有always、everysec和no三个可选值:
appendfsync always
同步写回,每个写命令执行完成后立刻同步到磁盘,优点是可靠性高和数据基本不丢失(最多丢失一条未完成写入的命令),缺点是每个写命令都要立刻落盘,频繁的fsync导致高磁盘I/O延迟,性能消耗最高;
工作机制,主线程将写命令追加到内存缓冲区aof_buf,调用write系统调用将aof_buf内容写入内核缓冲区,主线程立即调用fsync, 阻塞主线程直到数据完全写入磁盘;
appendfsync everysec
异步写回,每秒异步执行一次同步,平衡性能与数据安全性,优点是性能适中,同步操作由后台线程异步完成,主线程基本无阻塞,缺点是宕机时会丢失一秒内的数据;
工作机制,主线程将写命令追加到内存缓冲区aof_buf,调用write系统调用将aof_buf内容写入内核缓冲区,后台线程每秒触发一次fsync,将内核缓冲区的数据刷入磁盘,主线程仅在缓冲区满或后台线程未及时同步时短暂阻塞;
appendfsync no
异步写回,操作系统决定何时将内存缓冲区中的内容写入磁盘,优点是性能好,缺点是宕机时丢失数据较多;
工作机制,主线程将写命令追加到内存缓冲区aof_buf,调用write系统调用将aof_buf内容写入内核缓冲区,Redis不主动触发fsync,由操作系统决定同步时机(通常间隔30秒);
用户空间的缓冲区写入内核空间缓冲区,是指用户态程序通过write系统调用将数据从自身进程的用户空间缓冲区传输至操作系统内核管理的缓冲区中,以便内核进一步处理或执行底层I/O操作。这一过程的核心机制如下:
用户空间与内核空间的隔离性
用户空间,用户程序运行的环境,拥有独立的内存地址空间,无法直接访问硬件设备或其他进程的内存;
内核空间,操作系统内核运行的环境,拥有直接控制硬件、管理系统资源的权限,用户进程需通过系统调用(write、send)请求内核执行操作;
缓冲区的角色
用户缓冲区,位于用户空间,由应用程序自行分配和管理,用于临时存储待处理或待输出的数据;
内核缓冲区,位于内核空间,由操作系统管理,所有涉及硬件交互或跨进程通信的数据操作均需通过内核缓冲区中转;
当用户程序需将数据写入外部设备(如磁盘、网络)时:
系统调用触发,用户程序调用write()等函数,触发从用户态到内核态的切换;
数据拷贝,数据从用户缓冲区拷贝至内核缓冲区(例如套接字缓冲区、文件系统缓冲区)此拷贝操作由内核完成;
内核处理,内核根据数据类型和配置,将数据从内核缓冲区发送至目标设备(如通过网卡发送网络数据包);
设计目的
安全性,避免用户程序直接操作硬件或内核数据结构,防止系统崩溃或数据损坏;
性能优化,内核缓冲区可合并多次小数据写入请求,减少频繁I/O操作的开销;
异步处理,用户程序可继续执行后续任务,内核在后台完成实际I/O操作(如通过write()非阻塞模式);
性能瓶颈与优化
两次拷贝问题,传统流程中,数据需从用户缓冲区→内核缓冲区→硬件设备,存在两次内存拷贝;
总结来看,用户缓冲区写入内核空间是用户程序与操作系统协作完成I/O操作的核心机制,通过隔离权限和分层缓冲实现安全性与效率的平衡。
内核缓冲区数据的管理机制:
自动覆盖机制,当新的AOF数据写入内核缓冲区时,旧数据会被新数据覆盖,内核缓冲区本质上是操作系统维护的环形缓冲区,写入新数据时旧数据会被自动回收,无需手动删除;
操作系统调度,内核缓冲区数据由操作系统统一管理,磁盘刷写完成后,内核会自动将对应缓冲区标记为可复用状态,后续数据写入会直接覆盖这些区域;
执行BGREWRITEAOF指令(AOF重写)时,Redis会生成新的AOF文件并替换旧文件,此过程中旧AOF文件的内核缓冲区数据会在文件关闭后被操作系统自动回收;
2.2.2 AOF重写
AOF持久化,Redis不断将写命令记录到AOF文件中,随着Redis不断的进行,AOF文件会越来越大,占用服务器内存越大,以及AOF恢复时间也就越长。为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会自动启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,或者可以手动使用命令bgrewriteaof来重写AOF文件。
AOF重写只保留可以恢复数据的最小指令集,生成一个新的文件替换原来的AOF文件,这样不仅降低了文件的占用空间,同时更小的AOF也可以更快地被Redis加载。
重写过程不依赖原始AOF文件,直接读取内存数据库的键值数据生成新文件,避免逐条分析历史命令的开销。
AOF重写触发机制:
自动触发,在redis.conf配置文件中默认配置如下:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF文件同时满足redis.conf配置文件中的AOF自动重写条件,即当前AOF文件大小达到比上次重写后AOF文件大小增长的百分比,和当前AOF文件大小满足最小值限制64mb时,会自动重写AOF文件;
手动触发,客户端执行bgrewriteaof命令;
Redis 7.0之前的AOF重写原理:
当AOFRW被触发执行时,Redis首先会fork一个子进程进行后台重写操作,父子进程共享物理内存数据,重写操作读取执行fork那一刻的Redis数据库快照(所有键值对),然后将每一个键值对用一条命令记录到名为temp-rewriteaof-bg-pid.aof的临时AOF文件中,等到全部记录完后,就将新的临时AOF文件替换掉现有的AOF文件。所以,AOF 重写机制并不是直接修改现有的AOF文件,而是创建一个新的临时AOF文件,这个新的临时AOF文件会根据当前Redis数据库的状态来生成。
将重写操作写到新的AOF文件中,而不是直接对原AOF文件修改,当重写操作发生错误时,也即重写操作未完成,不会影响原AOF文件数据。
重写期间的同步机制:
AOF重写操作是在子进程中后台执行,主进程在AOF重写期间依然可以正常响应用户命令,如果主进程执行一条写命令,则会使用写时复制技术,父进程内存空间创建一个数据副本,在副本中执行写操作,这样不影响子进程重写AOF;
为了让子进程最终也能获取重写期间主进程产生的增量变化,主进程除了会将新执行的写命令写入(AOF缓冲区)aof_buf,还会写一份到(AOF重写缓冲区)aof_rewrite_buf中进行缓存;
在子进程重写的后期阶段,主进程会将aof_rewrite_buf中累积的数据使用pipe发送给子进程,子进程会将这些数据追加到临时AOF文件末尾,确保了临时文件和当前数据库的状态完全一致;
当子进程的临时AOF文件创建并重写完成后,父进程获得一个信号,父进程会将AOF重写缓冲区中的数据写入到临时AOF文件中,此时临时AOF文件数据就跟数据库数据保存一致,并且Redis会使用rename操作将临时AOF文件原子的重命名为server.aof_filename,此时原来的AOF文件会被覆盖,至此整个AOFRW流程结束;
redis7之前在重写时,会生成单一全量文件替换旧文件,会存在一下问题:
持久化漏洞,重写期间若发生故障,增量数据可能丢失;
性能波动,主进程需缓存增量数据到aof_rewrite_buf,可能因大key导致阻塞;
文件切换风险,删除旧 AOF 文件时若发生崩溃,可能导致数据无法恢复;
内存开销,重写期间新的写操作命令会保存在AOF缓冲区和AOF重写缓冲区中,写命令会保存两份冗余数据在内存中;
CPU开销,重写期间主进程需要花费CPU时间向aof_rewrite_buf写数据,并使用eventloop事件循环向子进程发送aof_rewrite_buf中的数据,在子进程执行重写操作的后期,会循环读取pipe中主进程发送来的增量数据,然后追加写入到临时AOF文件,消耗CPU资源;
磁盘IO开销,缓冲区aof_buf中的数据最终会被写入到当前使用的旧AOF文件中,产生磁盘IO,缓冲区aof_rewrite_buf中的数据也会被写入重写生成的新AOF文件中,产生磁盘IO,同一份数据会产生两次磁盘IO;
redis7开始通过分割AOF文件为多部分,减少全量文件替换带来的风险,同时支持更细粒度的数据恢复,文件类型有:
基础(BASE)AOF文件,对应传统AOFRW生成的文件,通过重写生成的最小命令集合,用于快速恢复数据;
增量(INCR)AOF文件,记录重写期间及后续新增的写操作,支持多个INCR文件共存,按序列编号;
清单文件(Manifest),记录所有AOF文件的元信息(如顺序、类型),确保文件切换的原子性;
分段写入策略,主进程将增量数据写入INCR文件,避免传统AOFRW中aof_rewrite_buf的潜在阻塞;
异步重写优化,子进程生成BASE文件期间,主进程通过管道(Pipe)实时传递增量数据,减少内存拷贝开销,提升重写效率;
redis7开始Multi Part AOF技术优势:
降低故障影响范围,故障时仅需回放最后未完成的INCR文件,而非全量AOF文件,缩短恢复时间,文件切换过程无锁化,避免旧文件删除导致的潜在数据丢失;
性能提升,主进程不再因aof_rewrite_buf累积大key而阻塞,写入延迟更稳定,增量文件按需生成,减少磁盘I/O压力;
管理自动化,通过Manifest文件自动追踪有效AOF文件,简化运维复杂度,支持历史文件自动清理策略,避免存储空间浪费;
Redis 7.0开始的AOF重写原理:
当AOFRW被触发执行时,主进程会fork一个子进程进行后台重写操作,并且主进程会同时打开一个新的INCR类型的AOF文件,父子进程共享物理内存数据;
在AOFRW重写期间,子进程会读取执行fork那一刻的Redis数据库内存快照,以命令的形式写入新的BASE AOF文件,子进程的重写操作完全是独立的,重写期间不会与主进程进行任何的数据和控制交互;
在AOFRW重写期间,主进程会继续处理新的请求,并将新的写操作指令使用写时复制技术,在主进程内存空间创建一个数据副本,在副本中执行写操作,将数据同步到aof_buf缓冲区,然后同步到新的INCR AOF文件;
AOFRW重写结束时,新的BASE AOF文件和新的INCR AOF文件就是完整的数据备份,代表了当前时刻Redis的全部数据;
当子进程完成重写BASE AOF时,主进程获得一个信号,主进程会负责更新manifest文件,将新BASE AOF和INCR AOF信息加入进去,并原子替换掉之前的增量AOF文件和基本AOF文件,将之前的BASE AOF和INCR AOF标记为HISTORY(这些HISTORY AOF会被Redis异步删除),一旦manifest文件更新完毕,就标志整个AOFRW流程结束,后台线程会自动清理history类型的文件;
如果重写失败,之前的基本文件和之前的增量文件,再加上新的增量文件代表完整的更新数据集;
Redis 7.0中BASE AOF文件只有重写时才会有,并且基本文件都是最小的指令集,重写BASE AOF文件的操作,并没有读取之前的BASE AOF文件和INCR AOF文件,而是将整个内存中的数据库内容用指令的方式重写了一个新的BASE AOF文件,这和快照有点类似。
Redis 7.0的版本重写过程中与之前相比的优势是,移除了AOF重写缓冲区,不需要将数据冗余的保存到内存,降低了内存开销,降低了CPU开销,不需要将缓冲区数据写入临时AOF文件,降低了磁盘IO开销。
Redis 7.0开始的AOF重写触发条件:
在Redis 7.0的MP-AOF机制中,触发AOF重写的条件是基于整个AOF数据的总量(包括BASE AOF文件和所有INCR AOF文件),触发重写条件与传统AOF保持一致,还是依赖auto-aof-rewrite-percentage和auto-aof-rewrite-min-size两个参数;
在MP-AOF设计中,AOF文件由BASE AOF文件和INCR AOF文件这两部分组成,AOF文件的大小是这两部分文件的大小总和;
2.3 数据恢复
Redis重启时,优先加载AOF文件,通过重新执行文件中的写命令恢复数据,因为AOF数据实时性更高。
2.4 检查修复AOF文件
####模拟持久化文件错误, 改写incr文件
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 22:16 appendonlyMangodir
-rw-r--r--. 1 root root 121 Mar 2 22:17 dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# cp -r appendonlyMangodir/ appendonlyMangodirbak
[root@MiWiFi-RD04-srv mangoRedis]# ll
total 4
drwxr-xr-x. 2 root root 127 Mar 2 22:16 appendonlyMangodir
drwxr-xr-x. 2 root root 127 Mar 2 22:18 appendonlyMangodirbak
-rw-r--r--. 1 root root 121 Mar 2 22:17 dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# rm -rf appendonlyMangodir
[root@MiWiFi-RD04-srv mangoRedis]# rm -f dumpMangoOne.rdb
[root@MiWiFi-RD04-srv mangoRedis]# mv appendonlyMangodirbak/ appendonlyMangodir
[root@MiWiFi-RD04-srv mangoRedis]# vim appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof
[root@MiWiFi-RD04-srv mangoRedis]#
####redis相关命令
[root@MiWiFi-RD04-srv bin]# ps -ef | grep redis
root 4942 3012 0 22:21 pts/1 00:00:00 grep --color=auto redis
[root@MiWiFi-RD04-srv bin]# redis-server mangoRedisConfig/redis7.conf
[root@MiWiFi-RD04-srv bin]# ps -ef | grep redis
root 4950 3012 0 22:21 pts/1 00:00:00 grep --color=auto redis
[root@MiWiFi-RD04-srv bin]# redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> quit
[root@MiWiFi-RD04-srv bin]# ll
total 29244
drwxr-xr-x. 2 root root 25 Mar 2 20:39 mangoRedisConfig
-rwxr-xr-x. 1 root root 6899128 Feb 20 22:17 redis-benchmark
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-check-aof -> redis-server
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-check-rdb -> redis-server
-rwxr-xr-x. 1 root root 7619024 Feb 20 22:17 redis-cli
lrwxrwxrwx. 1 root root 12 Feb 20 22:17 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 15418800 Feb 20 22:17 redis-server
drwxr-xr-x. 2 root root 25 Feb 23 01:16 zm
[root@MiWiFi-RD04-srv bin]# redis-check-aof
/mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof
Start checking Old-Style AOF
0x d7: Expected \r\n, got: 6c2e
AOF analyzed: filename=/mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof, size=244, ok_up_to=141, ok_up_to_line=51, diff=103
AOF /mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof is not valid. Use the --fix option to try fixing it.
[root@MiWiFi-RD04-srv bin]# redis-check-aof --fix
/mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof
Start checking Old-Style AOF
0x d7: Expected \r\n, got: 6c2e
AOF analyzed: filename=/mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof, size=244, ok_up_to=141, ok_up_to_line=51, diff=103
This will shrink the AOF /mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof from 244 bytes, with 103 bytes, to 141 bytes
Continue? [y/N]: y
Successfully truncated AOF /mangoRedis/appendonlyMangodir/appendonlyMangoOne.aof.1.incr.aof
[root@MiWiFi-RD04-srv bin]# redis-server mangoRedisConfig/redis7.conf
[root@MiWiFi-RD04-srv bin]# ps -ef | grep redis
root 4972 1 0 22:22 ? 00:00:00 redis-server *:6379
root 4978 3012 0 22:22 pts/1 00:00:00 grep --color=auto redis
2.5 AOF持久化特点
2.5.1 AOF优势
数据安全性高,AOF支持三种appendfsync持久化策略,fsync always,fsync everysec,fsync no,默认持久化策略是fsync everysec,fsync是使用后台线程执行的,当没有fsync持久化进行时,主线程将继续执行写入操作,最多仅丢失一秒的数据,故障后可通过日志恢复;
追加写入特性,AOF采用追加日志方式持久化,无需磁盘寻址,写入性能稳定,即使日志不完整,也能通过redis-check-aof工具修复;
自动重写压缩机制,当AOF文件内容过大,Redis在后台自动触发重写AOF文件,减少冗余命令的存储,生成包含最小指令集的新文件,Redis会切换这两个文件并开始向新文件追加;
日志可读性强,AOF文件以文本形式记录所有写操作,便于人工分析、故障排查或手动修复数据,譬如意外地使用FLUSHALL命令刷新了所有内容,只要在此期间没有重写日志,仍然可以通过停止服务器、删除最新命令并重新启动Redis来保存数据集;
2.5.2 AOF劣势
文件体积较大:相比RDB的二进制快照,AOF文件通常更大,占用更多磁盘空间;
恢复速度较慢:重启时需逐条执行日志中的命令重建数据,恢复效率低于RDB,尤其在大数据集场景下更明显;
潜在性能损耗:高频写入场景下(appendfsync always策略),频繁的磁盘操作可能影响Redis整体性能;
启动效率较低:数据集较大时,AOF的加载速度比RDB慢,可能导致服务启动延迟;
三 RDB与AOF
3.1 RDB和AOF比较
AOF和RDB持久性可以同时启用,Redis重启时会优先加载AOF文件,因AOF数据实时性更高,如果AOF文件不存在,那么会加载RDB文件。
AOF持久化的主要特点:
数据恢复,AOF文件精确地记录每一个写命令,因此在数据恢复时非常有用;
性能开销,相对于RDB而言,AOF可能会在写操作时带来更高的性能开销,因为它需要不断地将命令写入磁盘;
文件大小,AOF文件可能会比RDB文件大,因为AOF文件记录了所有的写操作,而RDB则是在某个时间点保存内存的完整快照;
3.2 RDB和AOF混合持久化
AOF和RDB持久性同时启用,redis服务器重启时会优先加载AOF文件,因为通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整,RDB持久化数据不够实时,但RDB文件更适用于备份数据库,AOF在不断变化不好备份;
AOF和RDB持久性同时启用,既能快速加载又能避免丢失过多的数据,在redis.conf配置文件中,属性值aof-use-rdb-preamble默认值为yes,开启混合持久化;
RDB模式 | AOF模式 | 混合模式 | |
数据恢复速度 | 快(二进制格式加载) | 慢(命令重放) | 快(RDB基础+AOF增量) |
数据安全性 | 低(可能丢失上次持久化开始到现在的数据) | 高(最多丢失1条或1秒的数据) | 高(最多丢失1条或1秒的数据) |
文件体积 | 小(全量压缩紧凑) | 大(写命令累计) | 小(RDB二进制紧凑内容+AOF增量写命令) |
混合持久化的文件结构,redis中混合持久化模式下生成的AOF文件分为两部分:
前半部分,以压缩二进制形式存储当前内存数据的RDB快照;
后半部分,记录RDB快照生成后的增量写操作AOF日志;
混合持久化模式下,redis服务重启时,先去加载rdb快照快速恢复基础数据集,然后再重放后续AOF日志补充增量修改;
简单来说:混合持久化方式产生的文件一部分是RDB格式作为文件头部,一部分是AOF格式增量写命令,AOF文件包括了RDB快照+AOF增量混写,即RDB快照做全量持久化,AOF做增量持久化;
四 纯缓存模式
纯缓存模式即是同时关闭RDB和AOF持久化功能,在配置文件进行相关配置:
关闭RDB持久化功能,save " "
关闭AOF持久化功能,appendonly no