6. 持久化(RDB/AOF)
众所周知,Redis是个将数据存在内存的数据库,当机器宕机的时候数据会全部丢失。因此Redis给了两种持久化方案,即:RDB(Redis DataBase)和 AOF(Append Only File)。
讲点题外话,相信接触过Redis的同学或多或少对于Redis的持久化方案有所了解。但是持久化并不是Redis的专属,也不是内存型数据的专属。当我们的数据需要备份的时候其实都需要持久化方案(可能不叫”持久化“而是叫”备份“),例如:双机热备、主从备份、跨机房备份等等。其实所谓持久化就是一份数据在发生意外情况时能够有另一份备份数据来继续支持外部使用。当然在整个过程中必然会出现源数据和备份数据不一致的情况。无需去纠结如何才能做到数据完全一致,而是要考虑真实的需求和技术之间的结合。
6.1 RDB
RDB持久化其实就是快照。顾名思义,就像我们拍照时,照片会记录我们某一瞬间的图像;而快照会记录我们某一瞬间的数据。
6.1.1 SAVE & BGSAVE
Redis可以使用SAVE命令和BGSAVE命令来将内存中的数据刷到磁盘中。
当执行SAVE命令时,Redis服务器会被阻塞,由于Redis是单工作线程模型的,这时客户端发送的所有命令都会被阻塞。所以一般情况我们不会执行SAVE命令。
BGSAVE就很好的规避了SAVE阻塞线程的情况,因为当调用BGSAVE的时候Redis会调用glibc的函数fork产生一个子进程,然后由这个子进程来实现快照持久化。这里会使用Linux操作系统的cow机制(在<1.4.4 Rehash>中提到过cow机制)来节省内存资源。
6.1.2 RDB对过期健的处理
由于Redis采用了惰性删除,所以内存中可能存在一些过期了但未被删除的数据,当执行SAVE或BGSAVE的时候程序会对数据库里的key进行检查,如果key已过期则不会保存到新创建的RDB文件中。
6.1.3 RDB文件载入数据库
在启动Redis服务器时,如果服务器开启了RDB功能,那么服务器将对RDB文件进行载入,载入期间服务器会一直处于阻塞状态,当载入完成后服务器才能对外提供服务。
注意:
如果同时开启了RDB和AOF,那么只会载入AOF,并不会载入RDB。
如果服务器以主服务器模式运行,RDB文件中的过期键会被丢弃。
如果服务器以从服务器模式运行,RDB文件中的过期健仍然会被加载到
6.1.4 RDB配置
redis.conf文件中saveparams的默认配置:
save 900 1
save 300 10
save 60 10000
具体的含义是,有以下三个条件之一,BGSAVE就会被执行:
在900秒之内,对数据库进行了至少1次修改
在300秒之内,对数据库进行了至少10次修改
在60秒之内,对数据库进行了至少10000次修改
redisServer结构体中会保存dirty(修改计数器)和lastsave(上一次执行保存的时间)来实现具体的判断。
6.2 AOF
AOF持久化其实就是命令执行日志。它是连续增量的,内部具体内容满足Redis通信协议。(具体RESP可在<3.3 redis的通信协议>中查看)
6.2.1 AOF原理
AOF持久化功能的实现大致分为命令追加、文件写入、文件同步三个步骤。
命令追加:当Redis服务器执行完一个命令之后,会以RESP格式追加到aof_buf缓冲区的末尾
文件写入:服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile来将aof_buf中的内容写到AOF文件。
文件同步:将写入AOF缓冲区的数据同步到磁盘上。
写入(write)和同步(fsync)其实是操作系统为了提高效率设置的一个机制,当调用write的时候,系统会将数据暂时保存在内存缓冲区,当缓冲区空间被填满或者超过了指定的时间,数据才会被真正写到磁盘上。这样的机制虽然提高了写入效率但带来了宕机数据丢失的风险,为此系统提供了fsync和fdatasync两个同步函数,可以让缓冲区的数据立即写到磁盘中。
小提示:在Redis4.0之后AOF Sync的操作被移到一个独立异步的线程去做,防止阻塞主线程。
配置文件中有对fsync各级别的详细说明(默认超1秒同步一次):
# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
6.2.2 AOF重写
随着Redis对外提供服务的时间增长,必然会导致AOF文件越来越大,不论是对于宿主机器的磁盘资源还是AOF重放来说,都不是一件好事。所以为了保证资源合理使用、服务正常运行,AOF有一个重写功能,重写完之后的AOF不会浪费空间,保证数据完整性的前提下使得新AOF文件体积小于原AOF文件。
整个AOF重写和原AOF文件半毛钱关系没有,简单来说就分了三步:
- 新建一个AOF文件重写文件。
- 保存一份当前Redis数据库的快照放在重写文件头部。
- 将新产生的日志append到重写文件中,替换原AOF文件。
6.2.3 AOF重写触发机制
Redis触发AOF重写的机制有三种:
Redis服务器接收到客户端发送的BGREWRITEAOF指令请求,如果当前AOF/RDB数据持久化没有在执行,那么就执行BGREWRITEAOF命令,否则Redis会等当前AOF/RDB数据持久化结束后再执行。
用户设置”config set appendonly yes“开启AOF的时,调用startAppendOnly函数会触发AOF重写。
在Redis配置文件redis.conf中,用户设置了auto-aof-rewrite-percentage(默认100)和auto-aof-rewrite-min-size(默认64mb)参数,并且当前AOF文件大小server.aof_current_size大于auto-aof-rewrite-min-size(server.aof_rewrite_min_size),同时AOF文件大小的增长率大于auto-aof-rewrite-percentage(server.aof_rewrite_perc)时,会自动触发AOF重写。
以下为配置文件中的说明:
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb