持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
备注:在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。
一、概念
Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis持久化分类:
RDB持久化:将当前数据保存到硬盘
AOF持久化:将每次执行的写命令保存到硬盘
备注:AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式。
二、RDB持久化
RDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。
RDB的持久化一般有两种方式,一种是同步的方式salve;一种是异步的方式bgsalve;
1.同步salve的方式
一般是手动触发salve命令。
介绍:save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。这种方式线上基本已经禁止使用。
2.异步bgsalve的方式
触发方式有好几种方式:
1)手动触发。
2)配置save m n,满足m秒内n次修改就执行bgsalve命令,进行持久化。
3)从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点。
4)执行shutdown命令时,自动执行rdb持久化。
介绍:bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求。
save m n的原理如下:每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。
(1)当前时间-lastsave > m
(2)dirty >= n
bgsalve执行流程:
1) Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
2) 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
3) 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令
4) 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
5) 子进程发送信号给父进程表示完成,父进程更新统计信息。
RDB文件是经过压缩的二进制文件,文件格式如下图所示(图片来源:《Redis设计与实现》):
其中各个字段的含义说明如下:
1) REDIS:常量,保存着”REDIS”5个字符。
2) db_version:RDB文件的版本号,注意不是Redis的版本号。
3) SELECTDB 0 pairs:表示一个完整的数据库(0号数据库),
同理SELECTDB 3 pairs表示完整的3号数据库;
只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);
如果Redis中所有的数据库都没有键值对,则这一部分直接省略。
其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;
0和3是数据库号码;pairs则存储了具体的键值对信息,
包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。
4) EOF:常量,标志RDB文件正文内容结束。
5) check_sum:前面所有内容的校验和;
Redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。
三、AOF持久化
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中;当Redis重启时再次执行AOF文件中的命令来恢复数据。与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案。
AOF的执行流程包括:
1.命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
备注:主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。
2.文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
备注:系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
3. 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。
备注:AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作。
重写主要采用:过滤掉过期数据、摒弃无效的命令、合并多条命令的方式来压缩数据。
可以采用手动触发bgrewriteaof和自动触发两种方式来启动文件重写。
对于AOF持久化来说,文件重写是里面的关键点,执行过程如下所示:
1. Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,
如果存在则bgrewriteaof命令直接返回,
如果存在bgsave命令则等bgsave执行完成后再执行。
2. 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的。
3. fork
1).父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。
Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。
2). 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。
4. 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。
5. 更新AOF文件
1). 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。
2). 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。
3). 使用新的AOF文件替换老文件,完成AOF重写。
四、RDB和AOF各有优缺点:
1.RDB持久化
优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。
缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
2. AOF持久化
与RDB持久化相对应,AOF的优点:在于支持秒级持久化、兼容性好。
缺点:是文件大、恢复速度慢、对性能影响大。
五、参考文献
http://www.redis.cn/topics/persistence.html
https://mp.weixin.qq.com/s/fpupqLp-wjR8fQvYSQhVLg
http://heylinux.com/archives/1932.html