1. 持久化选项
Redis提供了两种不同的持久化方法来将数据存储到硬盘里面。一种方法叫快照(snapshotting),它可以将存在于某一时刻的所有数据都写入硬盘里;另一种方法叫只追加文件(append-only file, AOF),它会在执行命令时,将被执行的写命令复制到硬盘里面。这两种持久化方法既可以同时使用,又可以单独使用。
2. RDB方式
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。如果在新的快照文件创建完毕之前,Redis、系统或者硬件这三者之中的任意一个崩溃了,那么Redis将丢失最近一次创建快照之后写入的所有数据。
2.1 快照时机
创建快照的办法有以下几种。
- 客户端可以通过想Redis发送BGSAVE命令来创建一个快照,对于支持BGSAVE命令的平台来说,Redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。执行BGSAVE后Redis会立即返回OK表示开始执行快照操作,如果想知道快照是否完成,可以通过LASTSAVE命令获取最近一次执行快照的时间,返回结果是一个UNIX时间戳。
- 客户端还可以通过向Redis发送SAVE命令来创建一个快照,接到SAVE命令的Redis服务器在快照创建完毕之前将不再响应任何额其他命令。SAVE命令并不常用,我们通常只会在没有足够内存去执行BGSAVE命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令。
- 如果用户设置了save配置选项,那么当任意一个save配置选项所设置的条件被满足时,Redis就会触发一次BGSAVE命令。save选项的格式为
save M N
:每当时间M内被更改的键的个数大于N时,即符合自动快照条件。 - 当执行FLUSHALL命令时,Redis会清除数据库中的所有数据。需要注意的是,不论清空数据库的操作是否出发了自动快照条件,只要自动快照条件不为空,Redis就会执行一次快照操作。当没有定义自动快照条件时,执行FLUSHALL则不会进行快照。
- 当Redis通过SHUTDOWN命令接收到关闭服务器的请求时,或者接收到标准TERM信号时,会执行一个SAVE命令,阻塞所有客户端,不在执行客户端发送的任何命令,并在SAVE命令执行完毕之后关闭服务器。
- 当一个Redis服务器连接另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候,如果主服务器目前没有在执行BGSAVE操作,或者从服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令。
2.2 快照原理
RDB是Redis默认采用的持久化方式,配置信息在配置文件redis.conf中。默认会将快照文件存储在Redis当前进程的工作目录中的dump.rdb文件中,可以通过配置dir和dbfilename两个参数分别配置指定快照文件的存储路径和文件名。快照的过程如下。
- redis使用fork函数复制一份当前进程的副本(子进程);
- 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
- 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成。
在执行fork的时候操作系统会使用写时复制(copy-on-write)策略,即fork()函数发生的那一刻父子进程共享同一内存空间,当父进程要更改某一片的数据时(如执行写命令),操作系统会将该片数据复制一份以保证子进程的数据不受影响,所以新的RDB文件存储的是fork()一刻的内存数据。
Redis在执行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的RDB文件替换成新的,也就是说任何时候RDB文件都是完整的。这时我们就可以通过备份RDB文件来实现Redis数据库备份。 RDB文件是经过压缩的二进制格式,所以占用的空间小于内存中的数据大小,更加利于传输。可以通过设置redcompression参数以禁用压缩节省CPU占用。
在只使用快照持久化来保存数据时,一定要记住:如果系统真的发送崩溃,用户将丢失最近一次生成快照之后更改的所有数据。因此,快照持久化只适用于那些即使丢失一部分数据也不会造成问题的应用程序。
如果Redis的内存占用量达到数十个GB,并且剩余的空闲内存并不多,或者Redis运行在虚拟机上面,那么执行BGSAVE可能会导致系统长时间地停顿,也可能引发系统大量地使用虚拟内存,从而导致Redis的性能降低至无法使用的程度。
3. AOF持久化
简单来说,AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化。因此,Redis只要从头到尾从新执行一次AOF文件包含的所有写命令,就可以回复AOF文件所记录的数据集。AOF持久化可以通过设置appendonly yes
选项打开。
AOF文件的同步频率通过appendfsync
选项设置。
选项 | 同步频率 |
---|---|
always | 每个Redis写命令都要同步写入硬盘,这样做会严重降低Redis的速度 |
everysec | 秒执行一次同步,显示地将多个写命令同步到硬盘 |
no | 让操作系统来决定何时进行同步 |
在向硬盘写入文件时,至少会发生3件事。当调用file.write()方法对文件进行写入时,写入的内容首先会被存储到缓冲区,然后操作系统会在将来的某个时候将缓冲区存储的内容写入硬盘,而数据只有在被写入硬盘之后,才算是真正地保存到了硬盘里面。用户可以通过file.flush()方法来请求操作系统尽快地将缓冲区存储的数据写入硬盘里,但具体何时执行写入操作仍然由操作系统决定。
为了解决AOF文件体积不断增大的问题,用户可以向Redis发送BGREWRITEAOF命令,这个命令会移除AOF文件中冗余命令来重写AOF文件,使AOF文件的体积变得尽可能地小。BGREWRITEAOF的工作原理和BGSAVE创建快照的工作原理非常相似:Redis会创建一个子进程,然后由子进程负责对AOF文件进行重写。
AOF持久化也可以通过设置auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
来自动执行BGREWRITEAOF。
4. 验证快照文件和AOF文件
无论是快照持久化还是AOF持久化,都提供了在需要系统故障时进行数据恢复的工具,Redis提供了两个命令行程序redis-check-aof和redis-check-dump,她们可以在系统故障发生之后检查AOF和快照的状态。并在有需要的时候对文件进行修复在不给定任何参数的情况下运行这两个程序,就可以看见他们的基本使用方法:
1 | redis-check-aof |
如果用户在运行redis-check-aof程序给定了–fix参数那么程序将对AOF文件进行修复。程序修复AOF文件的方法非常简单:他会扫描指定的AOF文件,寻找不正确或者不完整的命令,当发现第一个出错命令的时候,程序会删除出错命令已经出错命令后面的所有命令,只保留出错命令之前的正确命令,在大多数情况下,被删除的都是AOF文件末尾的不完整的写命令。
遗憾的是目前没有办法修复出错的快照文件。
参考资料
- 《Redis入门指南(第2版)》
- 《Redis实战》