1.RDB持久化
RDB持久化是把当前进程数据生成快照保存到硬盘的过程。触发RDB持久化的过程分为手动和自动触动。
触发的命令
1)save命令:阻塞当前Reid服务器,直到RDB过程完成为止,对于内存比较大的实例来说是会造成很长时间的阻塞
运行save命令对应的redis日志如下:
DB saved on disk.
2) bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。这个的阻塞只发生在fork阶段,一般时间很短。
可以手动触发bgsave命令,当然也可以后台自动触发
1)save m n 表示m秒内数据集存在n此修改时,就触发一次bgsave
2)如果从节点执行全量复制的操作,主节点需要自动执行bgsave生成RDB文件并发送给从节点
3)执行debug reload命令重新加载Redis时,也会自动触发save操作
4)默认情况下,执行shutdown命令时,如果没有开启AOF持久化命令功能则自动执行bgsave
bgsave的流程说明
1)首先执行bgsave命令会判断是否存在一个正在执行的子进程,如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_value_time.
5) 进程发送信号,告诉父进程完成了,父进程更新统计信息,具体见Persistenct下的Info rdb.*
RDB文件的处理
RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配置指定,也可以在运行期,执行config set dir{newdir},config set dbfilename{newFileName)运行期动态执行,当下次运行是RBB文件会保存在新的目录下
压缩:redis默认采用LZF算法对生成RDB文件做压缩处理,压缩后的文件远远小于这个内存大小,默认开启,可以通过参数config set /rdbcompression{yes | no } ,建议在线上开启,因为这样体积较小,方便传给子节点做全量同步
RDB的优缺点
优点:
1)RDB是一个紧凑压缩的二进制文件,代表的是Redis在某个时间点上的数据快照,非常适用于备份,全量复制的场景,并可以把RDB文件拷贝到远程机器或者文件系统保存,用于灾难恢复
2)redis恢复RDB的速度要快于AOF
缺点:
1)数据安全性,因为RDB不能做到实时同步,因为创建fork子进程是一个重操作,不能经常执行,效率很低
2)RDB使用二进制格式保存文件,redis版本存在多个格式的redis版本,存在老版本的rdb文件不能再新版本的redis中使用。
2.AOF持久化
以独立日志的方式记录每次的写命令,重启时候再重新加载AOF文件中的命令达到恢复数据的目的,AOF的只要作用就是解决了数据的实时性,目前已经是Redis持久化的主流方式。
开启AOF
修改配置 appendonly yes,默认不开启。
AOF文件名通过appendfilename配置设置,默认的文件名是appendonly.aof,保存路径同RDB持久化方式一致。
AOF的工作流程有哪些
1.命令写入aof缓冲区 (append命令)
aof写入的内容直接就是文本协议的格式(好处:直接将输入写到文件中,可以降低二次处理开销。可读性强)
为什么需要这一步写入缓存呢?
--这是因为写入缓存的话,可以比直接写入磁盘性能要好一些。可以让用户在性能和安全上去做选择
2.文件同步 (sync命令)
提供了多种文件同步的策略,由参数appendfsync控制
1)always 命令写入aof_buf中,调用系统fsync命令同步aof文件,fsync完成后线程返回(性能低)
2)everysec 命令写入aof_buf中,调用系统write操作,write完成后返回,fsync同步文件操作由其他线程专门每秒调一次(每秒一次,理论只会丢失最多一秒的数据,但是实际可能最多可以丢失两秒的数据,这是因为主线程会做判断,就是判断上一次的aof操作的时间距离现在是否是2秒钟了,如果超过两秒那么主线程也不接收数据了)
3)no 命令写入aof_buf中,就直接返回,不对aof_buf做fsync命令,同步操作由操作系统负责,通常最长周期是30秒。(从不,安全性太低)
write操作会触发延迟写机制,linux在内核提供页缓冲区用来提高磁盘的性能。write操作在写入系统缓冲区后直接返回,同步硬盘操作依赖于系统调度机制。比如,缓冲区写满了,或者特定周期刷新缓冲区
fsync针对单个文件操作。做强制硬盘同步,fsync将阻塞直到写入硬盘完成后返回,保证数据的持久化
除了write和fsync,还有sync,fdatasync操作
3.文件重写 (rewrite命令)
4.重启加载 (load命令)
5.具体流程:
1)将所有的写入命令都追加到aof_buf(缓冲区中)。
2)AOF缓冲区根据对应的策略向硬盘做同步操作
3)随着AOF的文件越来越大,需要对文件进行重写,达到压缩的目的
4)当redis服务器重启时,可以加载AOF文件进行数据恢复
重写机制
就是当命令不断写入AOF,文件会越来越大,为了解决这个问题,redis引入了aof重写机制压缩文件体积,AOF文件重写是把redis进程内的数据转换成写命令同步到新AOF文件的过程。
为什么文件会变少?
1)之前不要的数据不再写入文件
2)一些无效命令,如 del key hedl ky2 srem keys 等,重写使用进程内的数据直接生成,这样新的aof文件只保留最终数据的写入命令
3)多条命令会何为一条,但是为了防止单条命令过大导致客户端缓冲区溢出,对于List set hash zset等类型,以64个元素为界拆分为多条
触发命令
1)bgrewireof
2)根据auto-aof-rewrire-min-size(表示运行aof重写时文件最小体积)和 suto-aof-rewrite-percentage(代表当前aof文件空间和上一次重写后的比值 aof_current>auto_aof_rewrite_min_size && (aof_current_size-aof_base_size)/aof_base_size>=auto_aof_rewrite-size)参数确定自动触发,也就是说当前大小与上次aof之后的大小的差值与上一次aof后大小的比值是否大于比率。
重写的执行流程:
1)执行aof重写请求,如果当前进程正在执行AOF重写,请求不执行并返回如下响应:
ERR Background append only file rewriting already in progress
如果当前进程正在执行bgsave操作,重写命令延迟到bgsave完成后再进行
2)父进程执行fork创建子进程,开销等同于bgsave过程
3.1)主进程fork操作完成之后,继续响应其他命令,所有修改命令依然写入AOF缓冲区并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确性
3.2)由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据,但是由于父进程依然响应命令,redis使用AOF重写缓冲区保存这部分新数据,防止新AOF文件生成期间丢失这部分数据
4)子进程根据内存快照,按照命令合并规则写入新的AOF文件,每次批量写入磁盘数据量由配置aof_rewrite_incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞
5.1)新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体可见info persistence中的aof_*
5.2)父进程把AOF重写缓冲区的数据写入到新AOF文件
5.3)使用新AOF文件替换老文件,完成AOF重写
重启加载
RDB和AOF都可以用于数据恢复。
流程说明:
1)AOF持久化开启且存在AOF文件,优先加载AOF文件,打印如下日志:DB loaded from append only file : 5 seconds.
2)AOF关闭或者AOF文件不存在时候,加载RDB文件,打印如下日志: DB loaded from disk : 5 seconds
3) 加载AOF/RDB文件成功后,Redis启动成功
4)AOF/RDB文件存在错误时,Redis启动失败并打印错误信息
AOF的追加阻塞
当开启AOF持久化的时候,常用的同步策略就是everysec,用于平衡性能和数据安全性。对于这种方式,redis使用另外一条线程每秒执行fsync同步磁盘,当系统硬盘资源阻塞的时候,会造成主线程阻塞。
分析:1)主进程负责写入AOF缓冲区
2)AOF线程负责每秒执行一次同步操作,并记录最近一次的同步时间
3)主线程负责对比上次的AOF同步时间
如果距离上次同步成功在2秒内,则直接返回
如果距离上次同步成功在2秒外,则主线程阻塞,直到同步操作完成
所以,其实最多可能会丢失2秒的数据,而且如果硬盘fsync操作阻塞的话,可能会造成主线程阻塞。