Redis的数据机构、CAP+BASE理论我们在前面已经了解过了,现在咱们来了解下Redis的持久化和事务。

前言

在实际数据库选型过程中,为了灾备,可持久化基本是数据库的必要选项;Redis虽然是基于内存的,但是为了避免进程异常中断后数据的丢失,也提供了两种持久化方案,将内存中的数据写入磁盘,尽量减少数据的丢失。

Redis的持久化

Redis 提供了两种持久化方式:RDB和AOF。

RDB持久化方式:能够在指定的时间间隔能对Redis的数据进行快照存储,形成一个dump.rdb文件;是 Redis 默认采用的持久化方式。

AOF持久化方式:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

当然如果你希望你的数据全基于内存的,你也可以不使用任何持久化方式。

也可以同时开启两种持久化方式,在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整

RDB(Redis DataBase)

RDB持久化方式:能够在指定的时间间隔能对Redis的数据进行快照存储,形成一个dump.rdb文件。

RDB快照的实现过程

(1)Redis会单独创建(fork)一个子进程来进行持久化,会先将当前内存中的数据写入到磁盘的一个临时文件中。
(2)主进程是不进行任何IO操作,会继续接收并处理客户端发来的命令。
(3)当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,这样,一次RDB快照操作就完成了。

整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。

redis.conf的配置文件中,关于SNAPSHOTTING模块的全是RDB的相关配置。官方给出的配置文件中有这几种:
save 900 1:若900s(15min)内数据库改动了一次,则触发快照条件。
save 300 10:若300s(5min)内数据库改动了十次,则触发快照条件。
save 60 10000:若60s(1min)内数据库改动了一万次,则触发快照条件。

若不想使用持久化方式,可以配置成save ""

RDB快照的触发条件

总的来说,一般有以下四种触发快照条件

(1)达到配置文件中定义的规则;
(2)手动执行savebgsave命令;
(3)执行flushall命令;
(4)执行主从复制操作的时候。

save:执行save命令时Redis只管保存,其他不管,Redis进入阻塞状态。
bgsave:Redis会在后台异步进行快照操作,快照的同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间。
执行flushall命令,也会产生dump.rdb文件,但里面是空的,毫无意义。
主从复制的时候,master也会产生.rdb文件,slave节点会向master获取.rdb文件。

RDB快照的注意事项

  • Redis 在进行快照的过程中不会修改原 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候 RDB 文件都是完整的。

  • 这就使得我们可以通过定时备份 RDB 文件来实现 Redis 数据库的备份, RDB 文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输。

RDB持久化方式恢复数据

将RDB产生的备份文件(dump.rdb)移动到Redis安装目录并启动Redis服务即可。

RDB的优点

(1)RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份。
(2)RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心(可能加密),非常适用于灾难恢复。
(3)RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化Redis的性能。
(4)与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。

RDB的缺点

(1)RDB最后一次持久化后的数据可能丢失。
(2)如果你希望在Redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。
(3)RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

AOF(Appendonly File)

AOF持久化方式用日志的形式记录每次对服务器写的操作,当服务重启的时候会重新执行这些命令来恢复原始的数据,AOF的命令以Redis协议追加保存每次写的操作到文件末尾。
Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。

redis.conf的配置文件中,关于APPEND ONLY MODE模块的全是AOF的相关配置。官方给出的配置文件中默认是不开启的:

appendonly no:想要开启AOF持久化方式,修改成yes即可。
appendfsync always:同步持久化,每次发生数据变更会立即记录到磁盘,性能较差但数据完整性比较好。
appendfsync everysec:默认配置,异步操作,每秒记录数据变更;如果发生宕机最多丢失一秒的数据。
appendfsync no:从不主动同步。

AOF持久化方式恢复数据

AOF会产生文件(appendonly.aof),Redis启动时会重新执行这些命令来恢复原始的数据。

AOF重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。
举个例子, 如果你对一个计数器调用了 100 次 INCR key, 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。
然而在实际上, 只使用一条 SET key value [EX seconds] [PX milliseconds] [NX|XX]命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。

所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。那么在什么情况下会重写AOF文件呢:

1
2
3
4
5
6
# Redis会记住自从上一次重写后AOF文件的大小(如果自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
# 如果当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
# 同时需要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,但是已经达到百分比的情况。

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

AOF文件重写的过程:

(1)Redis 执行 fork() ,创建一个子进程,现在同时拥有父进程和子进程。
(2)子进程开始将新 AOF 文件的内容写入到临时文件。
(3)对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
(4)当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
(5)现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

AOF 重写总结:

(1)Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF进行重写重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合
(2)整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
(3)AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。

两种持久化方式的对比总结(取自官网)

  • 一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
  • 有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免 AOF 引擎的 bug 。

Redis事务

Redis的事务可以一次执行多个命令,本质是一组命令的集合。
一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞

以下摘自官网的解释:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。(注意,这里只是说执行的这个动作,即使事务中有某条/某些命令执行时失败了, 事务队列中的其他命令仍然会继续执行 —— Redis 不会停止执行事务中的命令。)

事务的开启:
MULTI:输入该命令开启事务。

命令的入队:
将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面

事务的执行:
EXEC:执行所有事务块内的命令。按顺序执行事务队列里面的命令,并返回执行结果。

关于事务的常见命令:
DISCARD:取消事务,放弃执行事务块内的所有命令。
EXEC:执行所有事务块内的命令。
MULTI:标记一个事务块的开始。
UNWATCH:取消 WATCH 命令对所有 key 的监视。
WATCH key [key ...]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

通常事务要配合watch和unwatch命令使用。这里的事务都是基于单机模式下的事务说明,关于集群的话事务的结果和操作有所不同。