1. 绪言
redis是一种内存数据库,它把数据存储在服务器的内存当中,这样极大地保证了redis数据库的性能,但也为数据安全带来了隐患——redis所在服务器重启或者发生宕机后,redis数据库里的所有数据将会全部丢失。庆幸的是,redis数据提供了一种持久化机制,可以将内存中的数据写入到硬盘中,有效降低了数据丢失的风险。
2. 什么是持久化
什么是持久化?简单来讲就是将数据放到断电后数据不会丢失的设备中,也就是我们通常理解的硬盘上。当开机后数据库再次启动时,可以从硬盘中恢复数据。redis提供两种方式进行持久化,一种是RDB持久化,这种方法是将Reids在内存中的数据库记录以拍摄快照的方式定时dump到磁盘上的RDB文件中进行持久化。另外一种是AOF(append only file)持久化,这种方法与RDB的保存整个redis数据库状态不同,AOF是通过保存对redis服务端的写命令(如set、sadd、rpush)来记录数据库状态的,即保存你对redis数据库的写操作。基于这两种持久化方式,Redis 提供了多种不同级别的持久化方式:
- RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
- AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
- Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
- 关闭持久化功能,让数据只在服务器运行时存在。
那么我们如何选择呢?我们先来分析RDB和AOF两种持久化方式。
3. RDB持久化
3.1 配置
RDB是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。我们可以通过在redis.conf文件中进行配置来设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存 save 300 10 #300秒内如超过10个key被修改,则发起快照保存 save 60 10000 #60秒内如果有10000个以上key被修改,则发起快照保存
dbfilename “dump.rdb” #持久化文件名称
dir ./ #持久化数据文件存放的路径 # bgsave发生错误,停止写入 stop-writes-on-bgsave-error yes # rdb文件采用压缩格式 rdbcompression yes # 对rdb文件进行校验 rdbchecksum yes
我们可以通过redis.conf文件来查看或修改配置,通过这种方式修改配置后需要重启redis。当然也还可以通过命令的方式来查看或修改配置:
127.0.0.1:6379> CONFIG GET save 1) "save" 2) "900 1 300 10 60 10000" 127.0.0.1:6379> CONFIG SET save "900 2" OK 127.0.0.1:6379> CONFIG GET save 1) "save" 2) "900 2"
通过命令的方式修改配置是即时生效的。
3.2 RDB持久化过程
当 Redis 进行持久化保存 dump.rdb 文件时, 服务器执行以下操作:
1)Redis 调用 fork() ,同时拥有父进程和子进程。
2)子进程将数据集写入到一个临时 RDB 文件中。
3)当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
如果刚写入某个十分重要的文件,需要立马进行持久化,我们可以通过调用 SAVE 或者BGSAVE , 手动让 Redis 进行数据集保存操作。两种方式区别如下:
SAVE是阻塞式的RDB持久化,当执行这个命令时redis的主进程把内存里的数据库状态写入到RDB文件(即上面的dump.rdb)中,直到该文件创建完毕的这段时间内redis将不能处理任何命令请求。
BGSAVE属于非阻塞式的持久化,它会创建一个子进程专门去把内存中的数据库状态写入RDB文件里,同时主进程还可以处理来自客户端的命令请求。但子进程基本是复制的父进程,这等于两个相同大小的redis进程在系统上运行,会造成内存使用率的大幅增加。推荐使用这种方法。
命令 |
save |
bgsave |
IO类型 |
同步 |
异步 |
是否阻塞 |
是 |
是(发生在fork期间) |
复杂度 |
O(n) |
O(n) |
优点 |
不消耗额外内存 |
不阻塞客户端命令 |
缺点 |
阻塞客户端命令 |
fork消耗额外内存 |
3.3 RDB的优劣势
RDB的优势:
1)RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份: 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。
2)RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心。
3)RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
4)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
RDB的劣势如下:
1)如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
2)每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
4. AOF持久化
4.1 配置
通过修改配置文件来打开 AOF持久化功能:
appendonly yes #设置打开AOF,默认是no appendfilename "appendonly.aof" #默认存放文件
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件。 appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。 appendfsync no #从不同步。高效但是数据不会被持久化。
4.2 AOF持久化过程
appendfsync有三个选项:always、everysec和no:
1) always: 选择always的时候服务器会在每执行一个事件就把AOF缓冲区的内容强制性的写入硬盘上的AOF文件里,可以看成你每执行一个redis写入命令就往AOF文件里记录这条命令,这保证了数据持久化的完整性,但效率是最慢的,却也是最安全的;
2) everysec: 配置成everysec的话服务端每执行一次写操作(如set、sadd、rpush)也会把该条命令追加到一个单独的AOF缓冲区的末尾,并将AOF缓冲区写入AOF文件,然后每隔一秒才会进行一次文件同步把内存缓冲区里的AOF缓存数据真正写入AOF文件里,这个模式兼顾了效率的同时也保证了数据的完整性,即使在服务器宕机也只会丢失一秒内对redis数据库做的修改;
3) appendfsync: 将appendfsync配置成no则意味redis数据库里的数据就算丢失你也可以接受,它也会把每条写命令追加到AOF缓冲区的末尾,然后写入文件,但什么时候进行文件同步真正把数据写入AOF文件里则由系统自身决定,即当内存缓冲区的空间被填满或者是超过了设定的时限后系统自动同步。这种模式下效率是最快的,但对数据来说也是最不安全的,如果redis里的数据都是从后台数据库如mysql中取出来的,属于随时可以找回或者不重要的数据,那么可以考虑设置成这种模式。
相比RDB每次持久化都会内存翻倍,AOF持久化除了在第一次启用时会新开一个子进程创建AOF文件会大幅度消耗内存外,之后的每次持久化对内存使用都很小。但AOF也有一个不可忽视的问题:AOF文件过大。你对redis数据库的每一次写操作都会让AOF文件里增加一条数据,久而久之这个文件会形成一个庞然大物。为了处理这种情况, Redis 支持一种有趣的特性:可以在不打断服务客户端的情况下,对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF命令, Redis 将生成一个新的 AOF 文件,这个文件包含重建当前数据集所需的最少命令。
那么,如果AOF文件出错了怎么办呢?
服务器可能在程序正在对 AOF 文件进行写入时停机,如果停机造成了 AOF 文件出错(corrupt),那么 Redis 在重启时会拒绝载入这个 AOF 文件,从而确保数据的一致性不会被破坏。
当发生这种情况时,可以用以下方法来修复出错的 AOF 文件:
第一步:为现有的 AOF 文件创建一个备份。
第二步:使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。执行以下命令:
redis-check-aof –fix
第四部:(可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
第五步:重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
5. 总结
一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性,你应该同时使用两种持久化功能。如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久化。有很多用户都只使用 AOF 持久化,但我们并不推荐这种方式:因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份,并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用 RDB 还可以避免 AOF 程序的 bug 。