一、RDB持久化
RDB持久化:将内存中的数据生成快照保存到硬盘上。
触发条件:
- 手动触发:通过redis-cli执行
BGSAVE
命令触发; - 自动触发:
– 配置文件中的“save m n”配置项:在m秒时间内发生了n次写入操作;
– 主从复制结构中,从节点进行全量复制时,会触发主节点执行BGSAVE
命令进行RDB持久化;
– 通过redis-cli执行shutdown命令时,如果没有开启AOF持久化功能则自动执行BGSAVE
进行RDB持久化。
1.1、RDB持久化过程
①某个条件触发RDB持久化;
②如果没有正在执行的子进程(如RDB/AOF子进程),Redis父进程fork一个子进程,用于持久化过程,fork过程中,Redis父进程会短暂阻塞。通过INFO stats
命令的“latest_fork_usec”记录上一次fork操作的耗时,单位是微秒;
③Redis父进程fork操作完成,将不再阻塞父进程,父进程可以继续响应其它命令;
④子进程生成RDB文件,根据Redis父进程内存中的内容生成快照,并替换原有RDB文件。LASTSAVE
和INFO persistence
命令的“rdb_last_save_time”记录上一次RDB持久化的时间戳;
⑤子进程执行完成,发送信号告知Redis父进程,Redis父进程更新INFO persistence
中的rdb相关信息。
1.2、RDB持久化相关配置
- 何时进行RDB持久化:“save m n”:在m秒时间内发生了n次写入操作;
- RDB文件存储路径:”dir /usr/local/redis/data”,Redis运行时通过
CONFIG SET dir ***
更改; - RDB文件名称:“dbfilename dump_6379.rdb”,Redis运行时通过
CONFIG SET dbfilename ***
更改文件名; - 是否对RDB文件进行压缩:“rdbcompression [yes|no]”;
- 是否对RDB文件进行CRC64校验:“rdbchecksum [yes|no]”;
1.3、RDB持久化优缺点
优点:
- RDB是Redis在某个时间点上内存数据的快照,非常适合备份、恢复和全量复制;
- RDB恢复数据的速度比AOF快很多;
缺点:
- RDB持久化每隔一段时间才会自动运行,无法做到实时/秒级持久化;
- 持久化过程需要fork操作,会阻塞Redis进程,频繁执行成本较高;
- 存在新老版本Redis无法兼容RDB文件的问题。
二、AOF持久化
AOF持久化:将写指令记录到特定文件,在重启或恢复数据时再逐条执行AOF文件中的写指令。解决了RDB持久化无法实现实时性的问题,是目前的主流持久化方式。
Redis重启时,会优先从AOF文件将数据加载到内存。
2.1、AOF持久化过程
①所有的写指令会追加(append)到aof_buffer缓冲区中;
②aof_buffer缓冲区中的内容会根据相应的策略同步到硬盘的AOF文件中;
③当AOF文件达到一定大小时会进行重写,达到压缩的目的。
2.2、缓冲区同步策略
Redis提供了三种同步策略:
- always:每次写指令写入aof_buffer都会调用系统fsync同步到AOF文件。虽然保证了数据安全,但需要更高性能的硬盘;
- everysec:写指令写入aof_buffer调用系统write操作,由专门线程每秒调用一次fsync同步文件操作。是Redis默认的配置,兼顾了性能和安全;
- no:写指令写入aof_buffer后调用系统write操作,由操作系统决定何时同步到硬盘。
关于系统调用write和fsync:
- write:延迟写,在将数据写入系统缓冲区后,由操作系统调度何时写入磁盘;
- fsync:强制硬盘同步。
2.3、AOF持久化相关配置
- appendonly [yes|no]:是否启用AOF持久化,默认关闭;
- appendfilename “appendonly_6379.aof”:AOF文件的名称;
- appendfsync [no|always|everysec]:缓冲区同步策略,默认为“everysec”
2.4、AOF追加阻塞
通常会使用“everysec”作为AOF持久化的缓存同步策略。这种方式中,Redis使用一个单独的线程每秒执行fsync操作,同步数据到硬盘。
当系统资源繁忙时,fsync同步操作也会受到影响,Redis主线程会对比上次AOF同步的时间,如果在2秒内,主线程会直接返回;如果超过2秒,主线程将会阻塞,直到同步操作完成。
所以在系统资源繁忙时,“everysec”配置最多可能丢失2秒数据,而不是1秒。
三、AOF重写机制
因为采用了AOF追加方式,会导致AOF文件越来越大。Redis提供了AOF重写机制,当AOF文件的大小超过所设定的阈值时,redis就会启动AOF重写进行文件的内容压缩,只保留可以恢复数据的最小指令集。
3.1、重写机制如何压缩文件
- 多条写指令合并为一个:
lpush list a、 lpush list b、 lpush list c
合并为lpush list a b c
; - 只保留数据的最终写入命令:
set a1;set b1;del a1
最终只保留set b1
。
3.2、重写触发的条件
- 手动执行
BGREWRITEAOF
命令; - 配置项自动触发。
相关的配置项:
auto-aof-rewrite-percentage 100
:代表当前AOF文件大小和上一次重写后AOF文件大小的比值;auto-aof-rewrite-min-size 64mb
:触发AOF重写的文件最小值,默认是64MB。
在INFO persistence
中记录了当前AOF文件大小:aof_current_size
和上一次重写后的AOF文件大小:aof_base_size
,自动触发时机:aof_current_size>auto-aof-rewrite-minsize
并且(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage
。
3.3、AOF重写过程
①某个条件触发AOF重写;
②如果没有正在执行的AOF重写过程或BGSAVE
操作,Redis父进程fork一个用于重写的子进程;
③Redis父进程会继续响应其他指令,默认的配置项“no-appendfsync-on-rewrite no”,决定了所有写指令仍然写入AOF缓冲区中,并根据同步策略将缓冲区数据写入硬盘,保证原来AOF机制正常运行;
同时由于Linux的写时复制(copy-on-write)特性,用于重写的子进程,只能共享fork操作时主进程的内存。而主进程仍然在响应指令,所以Redis使用“AOF重写缓冲区”(aof_rewrite_buffer)保存这些新的指令,防止重写期间的数据丢失;
④子进程根据当时的内存快照将写指令写入一个临时AOF文件,配置项“aof-rewrite-incremental-fsync”控制着每次写入硬盘的数据量为32MB,防止单次写入数据过多造成硬盘阻塞;
⑤子进程写入完成后通知Redis主进程,主进程更新INFO persistence
信息中的相关内容;
⑥主进程将aof_rewrite_buffer中的数据写入到临时AOF文件;
⑦使用临时AOF文件替换原AOF文件,重写完成。
四、持久化过程中的问题
4.1、fork造成的阻塞
在RDB持久化和AOF重写时都会调用fork创建子进程,而在fork操作时,Redis主进程会被阻塞,虽然时间很短。但对于流量很大的场景下,如果fork操作耗时超过1秒,对线上应用的延迟影响将会很明显。通过INFO stats
命令的“latest_fork_usec”指标可以查看最近一次fork操作的耗时,单位是微秒。
为了减少fork操作耗时,需要通过“maxmemory”参数控制Redis实例最大可用内存,因为Redis占用内存越多,fork操作需要复制的内存页表也越大。同时需要为系统预留足够的内存,避免物理内存不足导致fork操作失败。
设置sysctl vm.overcommit_memory=1允许内核可以分配所有的物理内存,避免Redis进程执行fork操作时因系统剩余物理内存不足而失败。
另外,由于Linux的大页内存(THP)机制,会在写时复制期间消耗更多的内存,Redis建议关闭THP。在CentOS7系统的禁用方法为:
echo "never" > /sys/kernel/mm/transparent_hugepage/enabled
echo "never" > /sys/kernel/mm/transparent_hugepage/defrag
4.2、监控子进程内存消耗
不管是RDB还是AOF重写都会消耗内存,消耗的内存量可以查询日志得出:
RDB相关日志:
8928:M 25 Dec 2020 14:05:19.620 * Background saving started by pid 8944
8944:C 25 Dec 2020 14:05:20.884 * DB saved on disk
8944:C 25 Dec 2020 14:05:20.888 * RDB: 1 MB of memory used by copy-on-write
8928:M 25 Dec 2020 14:05:20.962 * Background saving terminated with success
AOF重写相关:
8945:C 25 Dec 2020 14:05:22.670 * SYNC append only file rewrite performed
8945:C 25 Dec 2020 14:05:22.672 * AOF rewrite: 1 MB of memory used by copy-on-write
8939:S 25 Dec 2020 14:05:22.692 * Background AOF rewrite terminated with success
8939:S 25 Dec 2020 14:05:22.693 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
8939:S 25 Dec 2020 14:05:22.693 * Background AOF rewrite finished successfully
需要注意的是,AOF重写会用到AOF重写缓冲区,所以AOF重写时消耗的内存应该包括重写内存和AOF重写缓冲区占用内存。如上:1MB+0.00MB。
4.3、no-appendfsync-on-rewrite
该参数表示在AOF重写时不会将AOF缓冲区中的数据同步到磁盘。默认关闭,即在AOF重写时,会对AOF缓冲区中的数据做同步磁盘操作,这在很大程度上保证了数据的安全性。
但在数据量很大的场景,因为两者都会消耗磁盘IO,对磁盘的影响较大,可以将其设置为“yes”减轻磁盘压力,但在极端情况下可能丢失整个AOF重写期间的数据。