在Redis运行情况下,Redis 以数据结构的形式将数据维持在内存中,为了让这些数据在Redis 重启之后仍然可用,Redis 分别提供了RDB 和AOF 两种持久化模式。
- RDB持久化方式会在一个特定的间隔保存那个时间点的一个数据快照。
- AOF持久化方式则会记录每一个服务器收到的写操作。在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据。写操作命令记录的格式跟Redis协议一致,以追加的方式进行保存。
- Redis的持久化是可以禁用的,就是说你可以让数据的生命周期只存在于服务器的运行时间里。
- 两种方式的持久化是可以同时存在的,但是当Redis重启时,AOF文件会被优先用于重建数据。
首先我们来看一下Redis数据库在进行写操作时到底做了哪些事,主要有下面五个过程:
1.客户端向服务端发送写操作(数据在客户端的内存中)。
2.数据库服务端接收到写请求的数据(数据在服务端的内存中)。
3.服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
4.操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
5.磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。
RDB持久化机制
工作原理
- Redis调用fork(),产生一个子进程。
- 子进程把数据写到一个临时的RDB文件。
- 当子进程写完新的RDB文件后,把旧的RDB文件替换掉。
在Redis运行时,RDB程序将当前内存中的数据库快照保存到磁盘文件中,在Redis重启动时,RDB程序可以通过载入RDB文件来还原数据库的状态。RDB机制最主要的就是rdbSave和rdbLoad函数,前者将redis内存中数据加载到磁盘上,后者将在Redis重启时将数据恢复到redis内存中,注意rdbSave会阻塞主进程。
RDB持久化机制启用方式
在Redis的配置文件中开启如下设置:
# 格式为:save <seconds> <changes>
# 可以设置多个。
save 900 1 #900秒后至少1个key有变动
save 300 10 #300秒后至少10个key有变动
save 60 10000 #60秒后至少10000个key有变动
也可以通过手动执行SAVE和BGSAVE命令来执行保存快照到磁盘,SAVE和BGSAVE两个命令都会调用rdbSave函数,但它们调用的方式各有不同:
-
SAVE
直接调用rdbSave,阻塞Redis主进程看,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。 -
BGSAVE
则fork 出一个子进程,子进程负责调用rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。因为rdbSave 在子进程被调用,所以Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。
如下的伪代码演示了SAVE和BGSAVE的大体逻辑:
def SAVE():
rdbSave()
def BGSAVE():
pid = fork()
if pid == 0:
# 子进程保存RDB
rdbSave()
elif pid > 0:
# 父进程继续处理请求,并等待子进程的完成信号
handle_request()
else:
# pid == -1
# 处理fork 错误
handle_fork_error()
如果想禁用快照保存的功能,可以通过注释掉所有"save"配置达到,或者在最后一条"save"配置后添加如下的配置:
save ""
文件路径和名称
默认Redis会把快照文件存储为当前目录下一个名为dump.rdb的文件。要修改文件的存储路径和名称,可以通过修改配置文件redis.conf实现:
# RDB文件名,默认为dump.rdb。
dbfilename dump.rdb
# 文件存放的目录,AOF文件同样存放在此目录下。默认为当前工作目录。
dir ./
优点
- RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于做备份。你可以设定一个时间点对RDB文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本。
- 基于上面所描述的特性,RDB很适合用于灾备。单文件很方便就能传输到远程的服务器上。
方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上 - RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
- RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘操作。
缺点
-
如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
-
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
AOF持久化
AOF 则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到AOF文件,以此达到记录数据库状态的目的。AOF文件其实可以认为是Redis写操作的日志记录文件。快照并不是很可靠。如果你的电脑突然宕机了,或者电源断了,又或者不小心杀掉了进程,那么最新的数据就会丢失。而AOF文件则提供了一种更为可靠的持久化方式。每当Redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF里的命令会被重新执行一次,重建数据。
启用AOF
把配置项appendonly设为yes:
appendonly yes //启用aof持久化方式
# appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
# appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
## appendfsync no //完全依赖os,性能最好,持久化没保证
我们在文章的一开始就说了,Redis数据库在进行写操作时到底做了哪些事,主要有下面五个过程,其中有两步我们是要重点考虑的:
-
(WRITE)服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)这一步是每当有写操作执行时,Redis就会将数据写入到操作系统的缓存中
-
(SAVE)操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
而这一步的发送时机就是我们在配置文件中配置的appendfsync的值有关
-
no值
在这种模式下写磁盘只会在以下任意一种情况中被执行: Redis 被关闭、AOF 功能被关闭、系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行) 这三种情况下的SAVE 操作都会引起Redis 主进程阻塞。 -
everysec值
在这种模式中,SAVE 原则上每隔一秒钟就会执行一次,因为SAVE 操作是由后台子线程调用的,所以它不会引起服务器主进程阻塞,全性不错(最多丢失1秒的数据)。 -
always值
在这种模式下,每次执行完一个命令之后,写磁盘操作都会被执行,同时主进程会被阻塞,不能接受客户端命令请求
文件路径和名称
# 文件存放目录,与RDB共用。默认为当前工作目录。
dir ./
# 默认文件名为appendonly.aof
appendfilename "appendonly.aof"
日志重写
随着写操作的不断增加,AOF文件会越来越大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。
工作原理如下:
- redis调用fork ,现在有父子两个进程
- 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
优点
使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。
缺点
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
数据损坏修复
如果因为某些原因(例如服务器崩溃)AOF文件损坏了,导致Redis加载不了,可以通过以下方式进行修复:
- 备份AOF文件。
- 使用redis-check-aof命令修复原始的AOF文件:
redis-check-aof --fix
可以使用diff -u命令看下两个文件的差异。
使用修复过的文件重启Redis服务。
备份
建议的备份方法:
- 创建一个定时任务,每小时和每天创建一个快照,保存在不同的文件夹里。
- 定时任务运行时,把太旧的文件进行删除。例如只保留48小时的按小时创建的快照和一到两个月的按天创建的快照。
- 每天确保一次把快照文件传输到数据中心外的地方进行保存,至少不能保存在Redis服务所在的服务器。
网友评论