Redis 对于开发的同学都不陌生,它是当下最流行的键值(Key-Value)数据库,作为一种高性能的数据库,Redis将自己的数据存储在内存中而非磁盘,这就导致如果不想办法将存储在内存中的数据保存到磁盘里面,一旦服务器进程退出(服务宕机),内存中的数据也会一并丢失。
为了解决这个问题,Redis提供了两种持久化数据的方式,也就是我们常说的AOF和RDB。前者是利用AOF(Append only file)文件来记录Redis对数据的写操作,一旦出现进程退出的情况,数据会按照AOF文件的写操作自动恢复Redis;后者是利用RDB(Redis Database)二进制文件保存数据库状态,在进程退出后将读这个二进制文件来达到恢复数据数据的目的,本文主要总结一下这两种持久化方式并简述其原理。
简单来说,AOF持久化指的是通过保存Redis服务器所执行的写命令来记录数据库状态,如:
redis> SET msg "hello"
OK
复制代码
使用AOF后可以打开文件看下,内容类似于:
*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n
*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$5\r\nhello\r\n
复制代码
在这个AOF文件中,除了用于制定数据库的SELECT是服务器自动添加的之外,其他都是我们通过客户端发送的命令。其中,*3代表当前命令有三个部分,每个部分都是由+数字开头,后面跟着键、值或命令。这里的数字表示键、值或命令有多少字节,例如 3\r\nSET 代表这个命令有三个字节,
Redis的服务器进程就是一个事件循环,在这个循环中,文件事件负责接收客户端的命令请求,以及向客户端发送命令回复;因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到aof_buf
缓冲区里面,所以在服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile
函数,考虑是否要将aof_buf
缓冲区的内容写入和保存到AOF文件中。所以,AOF不会阻塞文件的写操作。
不过,AOF也有个显著的问题:如果在aof_buf
缓冲区内容写到AOF文件时,发生服务器宕机,那么这些写操作就会永久消失,为了解决类似的问题,Redis提供了三种写回策略:
appendfssync | 函数行为 |
---|---|
always | 每个事件循环都要将aof_buf缓冲区中所有内容写入到AOF文件中,并且同步AOF文件 |
everysec | 每个事件循环都要将aof_buf缓冲区中所有内容写入到AOF文件中,并且每隔1s就要在子线程中对AOF文件进行一次同步 |
no | 每个事件循环都要将aof_buf缓冲区中所有内容写入到AOF文件中,同步时机需要系统控制 |
总结AOF的写回策略:
配置项 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
always | 同步写回 | 可靠性高,基本不丢失 | 每次都同步导致影响很大 |
everysec | 每秒写会 | 性能适中 | 一旦服务退出,则会损失1s内数据 |
no | 操作系统写回 | 性能好 | 一旦服务退出,则会损失未写入的全部数据 |
如果要保证可靠性,那么就选择always或everysec,如果要保证高性能,推荐选择no,写回策略总是根据需求变更的。
一旦进程退出,就需要根据AOF载入命令并写回数据库,来还愿数据库状态:
以上面的例子来说,服务器首先读入并执行SELECT 0
,之后执行SET msg "hello"
,服务器的数据库就被还原到之前的状态了,非常简单。
因为AOF持久化是通过保存被执行的写命令来记录数据库状态,随着AOF文件的内容越来越多,文件的体积也会越来越大;随着AOF文件体积的越来越大,还原的时间也随之加长,为了解决AOF体积膨胀的问题,Redis提供了AOF文件重写的功能,通过AOF重写,Redis可以创建一个新的AOF文件来替代现有的AOF文件,同时新的AOF文件通常会比旧的小很多。 那么AOF是怎么实现文件的重写的呢?我们可以简单看一个例子:
redis> SADD colors "red" // {red}
(integer) 1
redis> SADD colors "yellow" "blue" // {red, yellow, blue}
(integer) 3
redis> SREM colors "red" // {yellow, blue}
(integer) 1
redis> SADD colors "orange" // {yellow, blue, orange}
(integer) 3
复制代码
为了记录这样一条命令,需要写入四次AOF文件;如果可以从Redis读取colors的值,用一条SADD colors "yellow" "blue" "orange"
来替代,所需要的命令就可以从四条减少为一条。从数据库中读取键所对应的值,然后用一条命令去记录键值对,替代之前记录这个键值对的多条命令,这就是AOF重写功能的原理。同时,AOF日志重写是通过后台子进程重写,不会阻塞主进程,具体过程各位同学可以自行探究~
RDB也被人称为内存快照,RDB 持久化产生的RDB文件是经过压缩的二进制文件,功过该文件可以还愿生成RDB文件时的数据库状态。
RDB文件是存储在磁盘上的,即使进程退出,只要RDB文件存在,Redis就可以利用RDB来还原数据库(自动还原)。
Redis生成RDB文件有两种方式,一种是 SAVE
,另一种是 BGSAVE
,两种方式区别如下: 1、SAVE
命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求; 2、BGSAVE
命令会派生出一个子进程,然后由子进程负责创建RDB文件,父进程继续处理命令请求; 另外,如果Redis同时启动了AOF持久化和RDB持久化,服务器会优先使用AOF文件来还原数据库状态;只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态,要注意这个问题哦~
因为BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以Redis允许用户配置save选项,让服务器每隔一段时间自动执行一次BGSAVE命令,举个例子:
save 900 1
save 300 10
save 60 1000
复制代码
那么只要满足三个条件中任意一个,BGSAVE就会被执行: 1、服务器在900秒之内,对数据库进行了至少1次修改; 2、服务器在300秒之内,对数据库进行了至少10次修改; 3、服务器在60秒之内,对数据库进行了至少1000次修改。
Redis4.0支持混合使用AOF文件和RDB来对Redis进行恢复,总结来说就是:在两次RDB文件创建之间,使用AOF文件来记录Redis写入的命令,通过这种方式,可以避免两次RDB写入的时候造成之间记录的数据丢失,以及只使用AOF文件导致AOF文件过大的问题,“成年人的世界就是「我全都要」”。
通过Redis的AOF持久化和RDB持久化可以避免进程退出导致的数据丢失问题,当然使用什么方式一定要结合业务的特性,使用Redis的新特性也可以取长补短,两种方式结合,不妨可以试试