前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、Redis持久化

Redis支持RDBAOP两种持久化模式,持久化功能有效避免进程退出造成的数据丢失问题。下次重启的时候利用之前持久化的文件即可实现数据恢复。

1.1、RDB

RDB持久化是将当前进程数据生成快照保存到硬盘的过程

RDB持久化的过程分为手动触发和自动触发

1.1.1、触发机制

1.1.1.1、手动触发

1.1.1.1.1、save命令(已经被丢弃)

save命令,阻塞当前的Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞。线上环境不建议使用,save命令已经被丢弃。

运行save命令Redis日志如下:

DB saved on disk
1.1.1.1.2、bgsave命令

Redis进程执行fork操作创建子进程,RDB持久化的过程由子进程负责,完成后自动结束,阻塞只发送在fork阶段,一般时间很短。因此Redis内部所有涉及RDB的操作都是bgsave命令的方式,save命令已经被丢弃。

Background saving started

1.1.1.2、自动触发

1、执行shutdown命令的时候,如果没有开启AOF持久化,则自动执行bgsave

2、执行debug relod命令的时候也会自动触发save操作

3、如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点

4、自动触发bgsave,使用save相关配置,如“save m n ”,表示m秒内数据集存在n次修改时,

#Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000

1.1.2、bgsave流程

1、执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回。

2、父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒。

3、父进程fork完成后,bgsave命令返,“Background saving started信息并不再阻塞父进程,可以继续响应其他命令

4、子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项。

127.0.0.1:6379> lastsave
(integer) 1523876908
127.0.0.1:6379> 

5、进程发送信号给父进程表示完成,父进程更新统计信息,具体见info Persistence下的rdb_*相关选项

WX20180417-103249@2x

1.1.3、RDB文件的处理

RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配置指定。

可以通过执行config set dir{newDir}config set dbfilename{newFileName}运行期动态执行,当下次运行时RDB文件会保存到新目录。

127.0.0.1:6379> config set dir /usr/local/redis-4.0.8/myrdb
OK
127.0.0.1:6379> config set dbfilename myrdb.rdb
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> 

1.1.4、RDB的优缺点

优点:

1、RDB是一个紧凑压缩的二进制文件,代表某个时间点的数据快照,远远小于内存大小,非常适用于备份,全量复制等场景,比如每6个小时执行bgsave备份,并把RDB文件拷贝到远程机器等位置,用于灾难恢复

2、Redis加载RDB恢复数据比AOF快多了

缺点:

1、RDB无法做到实时持久化/秒级持久化,因为每次bgsave都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高

1.2、AOF

AOF(append only file)持久化,AOF主要就是解决数据持久化的实时性,目前是Redis持久化的主流方式。 以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到数据恢复的目的

开启AOF功能需要进行配置,默认是不开启的,appendonly yes,观察redis.conf文件,默认文件名appendonly.aof(因为默认是关闭的所以没有写),保存路径同RDB持久化方式一致,通过dir配置指定

# 指定 AOF 文件名
appendfilename appendonly.aof
#修改为守护模式
daemonize yes
#设置进程锁文件
pidfile /usr/local/redis-4.0.8/redis.pid
#端口
port 6379
#客户端超时时间
timeout 300
#日志级别
loglevel debug
#日志文件位置
logfile /usr/local/redis-4.0.8/log-redis.log
#设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
databases 16
##指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
#save <seconds> <changes>
#Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
#指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,
#可以关闭该#选项,但会导致数据库文件变的巨大
rdbcompression yes
#指定本地数据库文件名
dbfilename dump.rdb
#指定本地数据库路径
dir /usr/local/redis-4.0.8/db/
#指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能
#会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有
#的数据会在一段时间内只存在于内存中
appendonly no
#指定更新日志条件,共有3个可选值:
#no:表示等操作系统进行数据缓存同步到磁盘(快)
#always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
#everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec

1.2、命令写入

1.2.1、AOF的工作流程

1、所有写命令会被追加aof_buf(缓冲区)中

2、AOF缓冲区根据对应的策略向硬盘做同步操作

3、随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的

4、当redis服务器进行重启时,可以加载AOF文件进行数据恢复

1.2.2、为什么要用文本协议格式

AOF命令写入的内容直接是文本协议格式,例如set hello world这条命令,在AOF缓存中会追加如下文本

1、文本协议有很好的兼容性。可读性,,方便直接修改和处理

2、开启AOF后,所有写入命令都包含追加操作,直接采用文本协议格式,避免了字符转换带来的二次开销。

1.2.3、AOF为什么会把命令放到aoc_buf缓存中

Redis使用单线程响应命令,如果每次AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载,还有好处,就是Redis提供了多种缓冲区到硬盘的同步的策略。在性能和安全性方面做出平衡

1.2、缓冲区到硬盘的同步的策略

Redis提供了AOF多种缓冲区同步文件策略,由参数appendfsync控制,不同值的含义如下

可配置值 说明
always 命令写入aof_buf后调用系统fsync操作同步到AOF文件,fsync完成后线程返回(慢,安全)
everysec 命令写入aof_buf后调用系统write操作,write完成后线程返回,fsync同步文件操作由专门的线程每秒调用一次(折衷,默认值)
no 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒,(快)

系统化调用writefsync说明:

write会触发延迟写机制,write操作在写入系统缓冲区后直接返回,同步硬盘操作依赖于系统调度机制,例如缓冲区写满了,同步文件之前,系统突然死机了,缓存区内数据将丢失

fsync针对单个文件操作(比如AOF文件)、做强制硬盘同步,fsync将阻塞直到写入硬盘完成后返回

配置为always,每次写入都要同步AOF文件,在一般的SATA硬盘上,Redis只能支持大约几百TPS写入,显然跟Redis高性能背道而驰,不建议配置

配置为everysec,是建议的同步策略,也是默认配置,做到监控性能,理论上只有在系统死机的情况下丢失1秒的数据

配置为no,由于操作系统每次同步AOF的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但是数据安全性无法保证

1.3、AOF重写机制

随着AOF文件越来越大Redis引入AOF重写机制,压缩文件体积。AOF文件重新就是讲Redis进程内的数据,转化为写命令同步到新的AOF文件的过程

1.3.1、重写后的AOF文件为什么可以变小

1、进程内已经超时的数据不再写入文件

2、旧的AOF文件含有无效命令,如del key1,hdel key2等,重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令,不会包含这些无用的命令。

3、多条写命令可以合并成一个,如:lpush list alpush list b ,转化为 lpush list a b,同时防止单挑命令多大造成客户端缓冲区溢出。

1.3.2、AOF重写过程的触发

1.3.2.1、手动触发

直接调用bgerwriteaof命令,不管有没有开启AOF都会执行

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
127.0.0.1:6379> 

1.3.2.2、自动触发

设置参数值 服务器在AOF功能开启的情况下,会维持以下三个变量:

1、记录当前`AOF`文件大小的变量`aof_current_size`。   

2、记录最后一次`AOF`重写之后,`AOF`文件大小的变量`aof_rewrite_base_size`。    

3、增长百分比变量`aof_rewrite_perc`。   

以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:

1、没有bgsave命令(RDB持久化)/AOF持久化在执行;

2、没有bgrewriteao命令f在进行;

3、当前AOF文件大小要大于server.aof_rewrite_min_size(默认为1MB),或者在redis.conf配置了auto-aof-rewrite-min-size大小。当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比(在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%)

2、重启加载

AOFRDB文件都可以用于服务器重启时的数据恢复

1、AOF持久化开启且存在AOF文件的时,优先加载AOF文件,打印如下日志:

* DB loaded from append only file : 5.841 seconds

2、AOF关闭或者AOF持久化虽然开启但是AOF文件不存在的时候,加载RDB文件,打印如下日志

* DB loaded from disk : 5586 seconds

3、加载 AOF或者RDB成功之后,Redis启动成功,如果AOF或者RDB文件存在错误,则Redis启动失败并报错

ContactAuthor