Redis提供了RDB持久化和AOF持久化
RDB机制的优势和策略
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
也是默认的持久化方式,这种方式就是讲内存中的数据以快照的方式写入到二进制文件中,默认的文件文件名为dump.rdb。
可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面就是默认的快照保存配置
save 900 1 //900秒内如果有超过一个key被修改,则发起快照保存
save 300 10 //300秒内如果超过10key被修改,则发起快照保存
save 60 10000
RDB文件保存过程
redis 调用fork,现在有了子进程和父进程
父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os写时复制机制(copy on write) 父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是共享页面。所以子进程的地址空间的内数据是fork时刻的整个数据库的一个快照。
当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主进程中保存快照的,由于redis是用一个主进程程来处理所有的client的请求,这种方式会阻塞所有的client请求,所以不推荐使用。
另外一点需要注意的是,每次快照持久化都是讲内存中的数据完整的写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
优势
一旦采用该方式,那么你的整个redis数据库将只包含一个文件,这样非常方便进行备份。
方便迁移,我们可以很容易的将一个RDB文件迁移到其他的存储介质上
RDB在恢复大数据集时的数据比AOF恢复数据要快
RDB可以最大化redis性能:父进程在保存RDB文件时唯一需要做的是fork出一个子进程,然后子进程会去处理接下来的所有保存工作,父进程无需执行任何磁盘I/O操作
劣势
如果你需要尽量避免在服务器故障时丢失数据,那么RDB不适合你。虽然redis允许你设置不同的保存点来控制保存RDB文件的频率,但是,应为RDB文件需要保存整个数据集的状态,所以它并不是一个轻松操作。假如我们至少5分钟才会保存一次数据(RDB文件)。在这种情况下,一旦发生服务器故障,你局有可能丢失几分钟的数据。
每次保存RDB的时候,redis都要fork()出一个子进程,并由子进程进行实际的持久化工作,在数据比较庞大的时候,fork可能会非常耗时,造成服务器在毫秒内停止处理客户端;如果数据集非常巨大,并且cpu实际非常紧张的话,这种耗时可能会长大整整一秒。
AOF文件保存过程
redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof).
当redis重启时会通过重新执行文件保存的写命令来在内存重建整个数据库内容。当然由于os会在内核中缓存write做的修改,所以可能不是立即写在磁盘上。这样aof方式持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)
appendonly yes //启用aof持久化方式
appendonly always/everysec/no 每次收到写命令局立即强制写入磁盘,最慢的,但是保证完全的持久化/每秒强制写入磁盘一次,在性能和持久化方面做了很好的折中/完全依赖os,性能最好,持久化没保证
aof的方式也同事带来了另外一个问题。持久化文件会变的越来越大。例如我们调用 incr test 命令100次,文件中必须保存全部的100条命令,其实有99条是多余的。因为要回复数据库的状态其实文件中保存一条set test 100 就够了。
为了压缩aof的持久化文件,redis 提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据以命令方式保存到临时文件中,最后替换原来的文件。具体过程如下
redis调用fork,现在有父子两个进程
子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出现问题。
当子进程把快照内容写入以命令方式写到临时文件中后,子进程发出信号通知父进程,然后父进程把缓存的写命令也写入到临时文件。
现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
需要注意的是重写aof文件的操作,并没有读取旧的aof文件,而是讲整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
优势
1、使用AOF持久化会让redis变得非常耐用:你可以使用fsync策略,比如无fsync,每秒,每次执行写入命令是fsync。AOF的默认策略为每秒钟fsync一次,在这种配置下,redis依然可以保持良好的性能,并且就算发生故障,也最多会丢失一秒钟的数据
2、AOF文件是一个只进行追加操作的日志文件,因此对AOF文件的写入不需要进行seek,即使日志因为某些原因而包含了未写入的完整命令,redis-check-aof工具也可以轻松修复这种问题。
redis可以在aof文件体积变得过大时,自动的在后台对AOF进行重写:重写后的新的AOF文件包含了恢复当前数据集所需最小命令集合。整个重写操作是绝对安全的,因为redsi在创建新的aof文件时,会继续将命令追加到先有的AOF文件里面,即使重写过程发生停机,现有的AOF文件也不会丢失。而一旦新AOF文件创建完毕,redis就会从旧的AOF文件切换到新的AOF文件,并开始对新的AOF文件进行追加操作。
3、AOF文件有序保存了对数据库执行的所有写入操作,这些写入操作以redis协议的格式保存,因此aof文件内容非常容易被人读懂,对文件进行分析也很轻松。导出aof文件也非常简单。举个例子,如果不小心执行了flushall命令,但是只要aof文件未被重写,那么知道停止服务器,移除aof文件末尾的flushall命令,并重启redis,就可以将数据恢复
劣势
1、相对于相同数据集来说,AOF文件体积通常大于RDB文件
2、根据所用是的fsync策略,AOF的速度可能会慢于RDB。在一般情况下,每秒fsync的性能依然非常高,而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
3、AOF有发生过bug:因为个别命令的原因,导致aof文件重新载入时,无法将数据集恢复成保存时的模样。(举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。
抉择
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
其余情况我个人喜好选择AOF