我们之前的学习Redis都是基于内存来存储数据的,那么内存存储数据虽然其速率比关系性数据库快非常多,但是它的缺点就是断电即失
,如果没有持久化的话,那么这内存中的数据将丢失,并且没有办法去恢复这些数据!
那么Redis中的持久化有两种方式:RDB和AOF,这也是面试经常问到的持久化方式!必须掌握这两种持久化方式!
10.1、RDB持久化
什么是RDB持久化?
RDB持久化就是通过硬盘来进行存储数据,当客户端在一定时间内操作次数满足了,那么这种机制就会将内存拍一张快照
,将这时刻的快照保存造硬盘中!
RDB工作原理
在进行RDB工作的时候,reids的主线程并不会进行任何io操作,而是然主线程fork一个子线程来完成该操作。
其RDB步骤:
-
Redis调用forks。同时拥有父进程和子进程
-
子进程将数据集写入一个临时RDB文件中
-
当子进程完成对新RDB文件的写入时,Redis用新的RDB文件替换为原来的RDB文件,并删除旧的RDB文件!
这种工作方式可以让Reids在从写复制(copy-on-write)机制中获利,因为子进程用来进行写操作,主线程依然可以处理客户端的请求!
-
save 命令
使用save命令,会立刻对当前内存中的数据进行持久化,但是会阻塞,期间不能进行其他的操作了
因为save命令是同步命令,会占用Redis的主进程。如果Redis数据非常多的时候,save命令执行速度就会非常慢,阻塞所有客户端的请求!
-
bgsave 命令
bgsave与save不同,bgsave是异步进行的,也就是说,持久化的过程不会占用主线程,而主线程依旧可以处理客户端的请求!
配置RDB
其实我们Redis中默认就是给我们开启RDB模式的,我们可以通过vim redis.conf来查看redis.conf文件
那么我们需要把这几个save放开,因为这里是注释,我们需要设置才会生效!
那么这些配置都是Redis默认帮我们配置好的,而且是默认开启的,那么我们就可以来测试一下这个RDB持久话
首先开启和连接到Redis服务端(这一步省略)
其次我们在5分钟内存储5个key
127.0.0.1:6379> set k1 v1 # 连续往redis中设置key
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK
此时我们再ls一下,如果再默认路径下有这个dump.rdb文件,那么将持久化成功!
可以看到,我们进行了5次写操作之后,就自动持久化成一个dump.rdb文件了
现在我们关闭服务器,然后再连一次服务器
127.0.0.1:6379> SHUTDOWN
not connected> exit
[root@localhost bin]# redis-server mconfig/redis.conf
[root@localhost bin]# redis-cli -p 6379
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k4"
4) "k1"
5) "k5"
可以看到,我们Redis服务器启动的时候,会自动读取dump.rdb文件,并将文件里面的数据还原出来!所以我们在重启服务器之后,数据还是存在的!
触发rdb持久化的条件
-
save的配置可以触发RDB的持久化
-
当我们退出Redis服务器的时候,会触发RDB
-
执行flushall命令,也会触发我们的rdb原则
证明第二条:
首先我们需要删除这个dump.rdb文件
[root@localhost bin]# rm -rf dump.rdb
[root@localhost bin]# ls
mconfig redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
然后重新连接到Redis服务端,连接完后直接shutdown命令退出
127.0.0.1:6379> SHUTDOWN
not connected> exit
此时再一次ls查看文件
那么证明Redis关闭时,会进行一次RDB持久化!
RDB的优点
引用官方文档的描述:
-
RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
-
RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
-
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
-
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
RDB的缺点
-
如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
-
RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
10.2、AOF持久化
AOF(Append-only file),只追加操作的文件,我们从上面RDB的缺点可以看出,它的持久化并不能保存短时间的数据,就比如说,你设置了5分钟操作100次就进行持久化,那么在99次操作完成后突然断电,服务器宕机,那么这99次操作或者这5分钟的数据全都丢失了,并且我们并不能恢复这5分钟内的数据!
对于这种情况,AOF可以做到每秒进行一次持久化, 它会将我们所有的命令都记录下来,history,恢复的时候就把这个文件全部再执行一遍!那么即使服务器宕机,断电了,我们只是丢失了那一秒的数据!
日志重写
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。
为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写。
基本配置
我们还是编辑这个redis.conf文件,查看AOF相关的配置!
no-appendfsync-on-rewrite no:
当我们选择appendfsync为everysec的时候,Redis会每一秒都进行一次数据同步,并且把数据同步到磁盘中,这个运行起来的效率有点慢,因为要等待Redis把数据同步到磁盘中,这过程时阻塞的。当然我们还有两种选择方案,一种是always,表示总是写入AOF文件,并完成同步,这就意味着只要写一行,那么Redis就同步一行,这样的效率是非常低的,但是这种方式数据的安全性是最高的!还有一种是选择no,意味着写入操作和同步磁盘这两个操作是异步的,那么不会发生阻塞,效率是最快的,同时数据安全性也是最差的,所有Redis选择了一种折中的方法,那就是everysec!
那么如果no-appendfsync-on-rewrite no设置为no是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。
设置为yes就相当于appendfsync设置为no, 这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据。
auto-aof-rewrite-percentage 100 : 当写入aof文件的大小是上一次写入的百分之100的时候(也就是两倍),会触发(rewrite)重写功能。 auto-aof-rewrite-min-size 64mb: 当aof文件的大小超过64mb的时候,就会触发重写功能!
重写(rewrite):
例如100条自增命令,重写时只需要一次set命令即可,省略了99条命令,减少了代码的冗余!
那么通过我们上面分析Redis一些配置,我们知道只要修改appendonly为yes,那么AOF持久化功能就可以开启,其他的都用默认的配置即可!
连接上Redis服务端后我们往里面设置一些值,测试这个AOF功能
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
用ls命令查看一下文件
可以看到确实有这个文件,我们可以查看一下里面的东西!
我们的命令确实被追加进去了,那么我们重启Redis服务端的时候,它首先会执行一次里面的语句,把数据还原出来!
如果AOF文件损坏了怎么办?
我们在安装redis的时候看过里面的目录,里面有一个AOF的修复工具
比如我们自己修改一下这个aof文件,让它报错
这时我们重启一下Redis服务端
可以看到,进程中没有Redis服务端,启动都启动不了!
那么我们用修复工具修复这个aof文件
再次查看aof文件
可以看到,设置k3的指令直接被删除了!
AOF工作原理
AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:
-
Redis 执行 fork() ,现在同时拥有父进程和子进程。
-
子进程开始将新 AOF 文件的内容写入到临时文件。
-
对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
-
当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
-
搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。
AOF优点
-
与RDB相比,当服务器宕机时候,最多只丢失一秒钟的数据,数据的完整性很高!
-
当aof文件过大,会触发自动重写; 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
-
AOF可以恢复出完整的数据集, 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF缺点
-
对于文件大小程度来说,AOF文件的体积往往比RDB文件的要大,而且AOF恢复数据的速度比RDB要慢
-
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
10.3、两种持久化的应用场景
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug。