Redis持久化 | RDB AOF | 常见问题
目录
RDB(Redis DataBase)
给什么内存数据做快照——(全量)
触发机制
RDB文件生成的时候会阻塞主线程吗?
关闭持久化命令
bgsave执行流程
RDB文件怎么配置?有哪些优缺点
优点:
缺点:
问题——数据丢失!
AOF(append only file)
使用方法
AOF工作流程
命令写入
AOF为什么选择直接采用文本协议方式?
AOF为什么将AOF命令要追加到缓冲中【aof_buf】?
AOF缓存同步文件策略有哪些?
怎么选择这些同步文件的策略呢?
重写机制 rewrite
为什么重写后的AOF文件会变小?
重写的时机?
好处:降低空间 & 体量小的AOF文件在redis中加载快。
PS:如果在Redis在进行AOF重写时,有写入操作,这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。
重启加载 load
如果文件损坏,那咋办?
RDB-AOF混合持久化
杂项问题
主线程、子进程和后台线程的联系与区别?
Redis持久化过程中有没有其他潜在的阻塞风险?
如何改善fork操作的耗时问题?
为什么主从库间的复制不使用 AOF?
Redis虽然是个内存数据库,但是Redis支持RDB和AOF两种持久化机制,将数据写往磁盘,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
RDB(Redis DataBase)
RDB持久化是把当前进程数据生成快照保存到硬盘的过程。所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。
有这样几个问题:
-
给什么内存数据做快照——(全量)
Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中。但是,RDB 文件就越大,往磁盘上写数据的时间开销就越大。
-
触发机制
除了手动触发RDB文件生成命令,自动触发RDB持久化机制。
-
save m n ——表示m秒内对数据集存在n次修改,自动触发bgsave
-
如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点。
-
执行debug reload命令重新加载Redis 时,也会自动触发save操作。
-
默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave。
-
RDB文件生成的时候会阻塞主线程吗?
-
save【线上环境不推荐】:在主线程中执行,会导致阻塞。对于内存比较大的实例会造成长时间阻塞。
-
bgsave【推荐】:为了快照而暂停写操作,肯定是不能接受的。所以这个时候,Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
-
关闭持久化命令
save ""
-
bgsave执行流程
bgsave子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 B),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
-
RDB文件怎么配置?有哪些优缺点
dbfilename配置进行指定,可以通过执行config set dir {newDir}和config set dbfilename (newFileName}运行期动态执行,当下次运行时RDB文件会保存到新目录。
【推荐】Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数config set rdbcompression { yes |no}动态修改。虽然压缩RDB会消耗CPU,但可大幅降低文件的体积,方便保存到硬盘或通过网络发送给从节点。
如果 Redis加载损坏的RDB文件时拒绝启动,并打印如下日志:Short read or OOM loading DB. Unrecoverable error,aborting now.可以使用Redis提供的redis-check-rdb工具(老版本是redis-check-dump)检测RDB文件并获取对应的错误报告。
优点:
RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。
比如每隔几小时执行bgsave备份,并把 RDB文件拷贝到远程机器或者文件系统中(如hdfs),,用于灾难恢复。
Redis加载RDB恢复数据远远快于AOF的方式。
缺点:
-
RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
-
RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。
-
问题——数据丢失!
我们先在 T0 时刻做了一次快照(下一次快照是T4时刻),然后在T1时刻,数据块 5 和 8 被修改了。如果在T2时刻,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复。此时,数据块 5 和 8 的修改值因为没有快照记录,就无法恢复了。
如果想丢失较少的数据,那么T4-T0就要尽可能的小,但是如果频繁地执行全量
快照,也会带来两方面的开销:
-
频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
-
另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁fork出bgsave 子进程,这就会频繁阻塞主线程了。
正因为有这些缺点,为此Redis提出AOF持久化来解决。
AOF(append only file)
使用方法
-
开启AOF功能需要设置配置:appendonly yes,默认不开启。
-
AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同RDB持久化方式一致,通过dir配置指定。
AOF工作流程
AOF的工作流程主要是4个部分:命令写入( append)、文件同步( sync)、文件重写(rewrite)、重启加载( load)。
命令写入
AOF命令写入的内容直接是RESP文本协议格式。
例如lpush dura A B这条命令,在AOF缓冲区会追加如下文本:
*3\r\n$6\r\nlupush\r\n$5\r\ndura\r\n$3\r\nA B
AOF 日志的内容。其中,“*3”表示当前命令有三个部分,每部分都是“+数字”开头,后面紧跟着
具体的命令、键或值。这里,“数字”表示这部分中的命令、键或值一共有多少字节
如:由“3 set”表示这部分有 3 个字节,也就是“set”命令。
AOF为什么选择直接采用文本协议方式?
文本协议具有很好的兼容性。开启AOF后,所有写入命令都包含追加操作,直接采用协议格式,避免了二次处理开销。文本协议具有可读性,方便直接修改和处理。
AOF为什么将AOF命令要追加到缓冲中【aof_buf】?
Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
AOF缓存同步文件策略有哪些?
配置参数appendfsync
其中有三种可选择的选项
-
always:同步写回,每个写命令执行完,立马同步地将日志写回磁盘。
-
everysec:每秒写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘。
-
no:操作系统控制的写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘,通常同步周期最长30秒。
怎么选择这些同步文件的策略呢?
【不推荐】配置为always时,每次写入都要同步AOF文件,在一般的SATA 硬盘上,Redis只能支持大约几百TPS写入,显然跟Redis高性能特性背道而驰,不建议配置。
配置为no,由于操作系统每次同步AOF文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。
配置为everysec,是建议的同步策略,也是默认配置,做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失1秒的数据。(严格来说最多丢失1秒数据是不准确的)
-
高性能,就选择 no 策略。
-
如果想要得到高可靠性保证,就选择always 策略。
-
如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择everysec 策略。
重写机制 rewrite
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入AOF重写机制压缩文件体积。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
为什么重写后的AOF文件会变小?
-
进程内已经超时的数据不再写入文件。
-
旧的AOF文件含有无效命令,重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
-
多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为: lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。
重写的时机?
-
手动触发:bgrewriteaof
-
自动触发:根据auto-aof-rewrite-min-size和 auto-aof-rewrite-percentage参数确定自动触发时机。
-
auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
-
auto-aof-rewrite-percentage :代表当前AOF 文件空间(aof_currentsize)和上一次重写后AOF 文件空间(aof_base_size)的比值。
-
好处:降低空间 & 体量小的AOF文件在redis中加载快。
PS:如果在Redis在进行AOF重写时,有写入操作,这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。
重启加载 load
AOF和 RDB 文件都可以用于服务器重启时的数据恢复。
-
当AOF和RDB文件同时存在时,先加载AOF
-
如果关闭了AOF,就加载RDB
-
加载成功的情况下,Redis重启成功
-
AOF/RDB存在错误,会打印错误信息。
如果文件损坏,那咋办?
加载损坏的AOF 文件时会拒绝启动,对于错误格式的AOF文件,先进行备份,然后采用redis-check-aof --fix命令进行修复,对比数据的差异,找出丢失的数据,有些可以人工修改补全。
AOF文件可能存在结尾不完整的情况,比如机器突然掉电导致AOF尾部文件命令写入不全。Redis为我们提供了aof-load-truncated 配置来兼容这种情况,默认开启。加载AOF时当遇到此问题时会忽略并继续启动,同时如下警告日志。
RDB-AOF混合持久化
aof-use-rdb-preamble 打开混合开关。
该状态开启后,如果执行bgrewriteaof命令,则会把当前内存中已有的数据弄成二进程存放在aof文件中,这个过程模拟了RDB生成的过程,然后Redis后面有其他命令,在触发下次重写之前,依然采用AOF追加的方式
杂项问题
主线程、子进程和后台线程的联系与区别?
-
进程和线程的区别
从操作系统的角度来看,进程一般是指资源分配单元,例如一个进程拥有自己的堆、栈、虚存空间(页表)、文件描述符等;而线程一般是指 CPU 进行调度和执行的实体。一个进程启动后,没有再创建额外的线程,那么,这样的进程一般称为主进程或主线程。
Redis 启动以后,本身就是一个进程,它会接收客户端发送的请求,并处理读写操作请求。而且,接收请求和处理请求操作是 Redis 的主要工作,Redis 没有再依赖于其他线程,所以,一般把完成这个主要工作的 Redis 进程,称为主进程或主线程。
-
主线程与子进程
通过fork创建的子进程,一般和主线程会共用同一片内存区域,所以就需要使用到写时复制技术确保安全。
-
后台线程
从 4.0 版本开始,Redis 也开始使用pthread_create 创建线程,这些线程在创建后,一般会自行执行一些任务,例如执行异步删除任务
Redis持久化过程中有没有其他潜在的阻塞风险?
当Redis做RDB或AOF重写时,一个必不可少的操作就是执行fork操作创建子进程,对于大多数操作系统来说fork是个重量级错误。虽然fork创建的子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。例如对于10GB的Redis进程,需要复制大约20MB的内存页表,因此fork操作耗时跟进程总内存量息息相关,如果使用虚拟化技术,特别是Xen虚拟机,fork操作会更耗时。
Info status
对于高流量的Redis实例OPS可达5万以上,如果fork操作耗时在秒级别将拖慢Redis几万条命令执行,对线上应用延迟影响非常明显。正常情况下fork耗时应该是每GB消耗20毫秒左右。可以在info stats统计中查latest_fork_usec指标获取最近一次fork操作耗时,单位微秒。
如何改善fork操作的耗时问题?
-
优先使用物理机或者高效支持fork操作的虚拟化技术
-
控制Redis实例最大可用内存,fork耗时跟内存量成正比,线上建议每个Redis实例内存控制在10GB 以内。
-
降低fork操作的频率,如适度放宽AOF自动触发时机,避免不必要的全量复制等。
为什么主从库间的复制不使用 AOF?
-
RDB 文件是二进制文件,无论是要把 RDB 写入磁盘,还是要通过网络传输 RDB,IO效率都比记录和传输 AOF 的高。
-
在从库端进行恢复时,用 RDB 的恢复效率要高于用 AOF。