Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)的里边,数据保存到硬盘的过程就称为“持久化”效果。

redis有两种持久化功能,一种是“快照持久化(RDB)”,一种是“AOF持久化”。


 Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制 来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。



Redis 的持久化机制有两种,第一种是快照,第二种是 AOF 日志。快照是一次全量备 份,AOF 日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。AOF 日志在长期的运行过程中会 变的无比庞大,数据库重启时需要加载 AOF 日志进行指令重放,这个时间就会无比漫长。 所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身。



快照原理

我们知道 Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作 和内存数据结构的逻辑读写。

在服务线上请求的同时,Redis 还需要进行内存快照,内存快照要求 Redis 必须进行文 件 IO 操作,可文件 IO 操作是不能使用多路复用 API。

这意味着单线程同时在服务线上的请求还要进行文件 IO 操作,文件 IO 操作会严重拖 垮服务器请求的性能。还有个重要的问题是为了不阻塞线上的业务,就需要边持久化边响应 客户端请求。持久化的同时,内存数据结构还在改变,比如一个大型的 hash 字典正在持久 化,结果一个请求过来把它给删掉了,还没持久化完呢,这尼玛要怎么搞?

那该怎么办呢?

Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化,这个机制 很有意思,也很少人知道。多进程 COW 也是鉴定程序员知识广度的一个重要指标。



fork(多进程)

Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进 程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代 码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操 作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复 制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的, 还是进程产生时那一瞬间的数据。



随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增 长。但是也不会超过原有数据内存的 2 倍大小。另外一个 Redis 实例里冷数据占的比例往 往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页 面。每个页面的大小只有 4K,一个 Redis 实例里面一般都会有成千上万的页面。

子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再 也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安 心的遍历数据了进行序列化写磁盘了。



AOF 原理

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的 指令记录。

假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过 对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内 存数据结构的状态。

Redis 会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文 本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。这样即使遇到突发宕机,已 经存储到 AOF 日志的指令进行重放一下就可以恢复到宕机前的状态。

Redis 在长期运行的过程中,AOF 的日志会越变越长。如果实例宕机重启,重放整个AOF 日志会非常耗时,导致长时间 Redis 无法对外提供服务。所以需要对 AOF 日志瘦 身。



AOF 重写

Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理就是开辟一个子进 程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。 序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加 完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

fsync

AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将 内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘 的。

这就意味着如果机器突然宕机,AOF 日志内容可能还没有来得及完全刷到磁盘中,这个 时候就会出现日志丢失。那该怎么办?

Linux 的 glibc 提供了 fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷到磁 盘。只要 Redis 进程实时调用 fsync 函数就可以保证 aof 日志不丢失。但是 fsync 是一个 磁盘 IO 操作,它很慢!如果 Redis 执行一条指令就要 fsync 一次,那么 Redis 高性能的 地位就不保了。

所以在生产环境的服务器中,Redis 通常是每隔 1s 左右执行一次 fsync 操作,周期 1s是可以配置的。这是在数据安全性和性能之间做了一个折中,在保持高性能的同时,尽可能 使得数据少丢失。

Redis 同样也提供了另外两种策略,一个是永不 fsync——让操作系统来决定合适同步磁 盘,很不安全,另一个是来一个指令就 fsync 一次——非常慢。但是在生产环境基本不会使 用,了解一下即可。



运维



快照是通过开启子进程的方式进行的,它是一个比较耗资源的操作。

1、遍历整个内存,大块写磁盘会加重系统负载
2、AOF 的 fsync 是一个耗时的 IO 操作,它会降低 Redis 性能,同时也会增加系统 IO 负担



所以通常 Redis 的主节点是不会进行持久化操作,持久化操作主要在从节点进行。从节 点是备份节点,没有来自客户端请求的压力,它的操作系统资源往往比较充沛。 但是如果出现网络分区,从节点长期连不上主节点,就会出现数据不一致的问题,特别是在网络分区出现的情况下又不小心主节点宕机了,那么数据就会丢失,所以在生产环境要做好实时监控工作,保证网络畅通或者能快速修复。另外还应该再增加一个从节点以降低网络分区的概率,只要有一个从节点数据同步正常,数据也就不会轻易丢失。 


1.snap shotting快照持久化

该持久化默认开启,一次性把redis中全部的数据保存一份存储在硬盘中,如果数据非常多(10-20G)就不合适频繁操作该持久化操作。

如果对redis有数据操作,就会根据redis的配置文件指定的文件名和路径生成rdb文件,先来看一下redis配置方面的截图说明

redis快照怎么触发 redis快照与aof原理_Redis

再看一下在redis目录下生成的rdb文件

redis快照怎么触发 redis快照与aof原理_数据结构与算法_02

可以使用vim命令对dump.rdb文件编辑看一下内容

redis快照怎么触发 redis快照与aof原理_操作系统_03

注意:如果您细心的发现,在对redis客户端进行数据操作之后,再次进行对dump.rdb文件进行编辑查看,文件中可能会缺少最近的操作记录,这与配置文件中备份的频率有关,下面看一下截图

redis快照怎么触发 redis快照与aof原理_数据库_04



save 900 1             #900 秒内如果超过 1 个 key 被修改,则发起快照保存
save 300 10            #300秒超过10个key被修改,发起快照
save 60 10000            #60秒超过10000个key被修改,发起快照



以上三个save的意思都有了相应的说明,数据修改的频率非常高,备份的频率也高,数据修改的频率低,备份的频率也低。

如果发现dump.rdb文件缺少了最近的记录,那么在这补充一种手动持久化方式,可以立即看到效果,执行此命令



./redis-cli bgsave #异步保存



其次还包括一些其他的手动命令



./redis-cli shutdown    #同步保存到服务器并关闭redis服务器
./redis-cli lastsave    #返回上次成功保存到磁盘的unix时间戳
./redis-cli bgrewriteaof  #当日志文件过长时优化AOF日志文件存储



由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式

2.append only file(AOF持久化)

AOF持久化本质:把用户执行的每个“写”指令(添加/修改/删除)都备份到文件中,还原数据的时候就是执行具体写指令而已。

开启AOF持久化(会清空redis内部的数据,最好在redis使用之前就开启它)

我们在redis.conf配置文件中可以找到它:

redis快照怎么触发 redis快照与aof原理_Redis_05

修改完成配置之后重启redis

redis快照怎么触发 redis快照与aof原理_数据结构与算法_06

redis快照怎么触发 redis快照与aof原理_Redis_07

再次启动测试一下是否有数据

redis快照怎么触发 redis快照与aof原理_redis快照怎么触发_08

看一下目录下自定义的aof文件,目前得大小是0

redis快照怎么触发 redis快照与aof原理_操作系统_09

操作之后

redis快照怎么触发 redis快照与aof原理_操作系统_10

vim查看一下

redis快照怎么触发 redis快照与aof原理_数据结构与算法_11

所有指令全部写入文件

在redis.conf中,可以调整AOF备份形式:

redis快照怎么触发 redis快照与aof原理_Redis_12



always          一写指令就备份一次。这样做虽然安全,但是系统性能会降低。不推荐使用
everysec         每一秒中备份一次。不管一秒钟变化了多少key,只备份一次,性能得到一定的保护。推荐使用。
no            会查看当前服务器状态,如果状态良好,就进行备份(随机)。这种备份方式数据是没有保证的。



对比下来,性能:always<everysec<no,而数据安全:always>everysec>no。

举例说明一下,这里就暂时不操作了,您可以拿redis的自增来操作:incr  num  ,执行此命令10次,然后查看一下aof文件,发现aof文件保存的是最终的数值,而且是set命令,这样就节省了空间,下面说一下就是把AOF备份文件中所有的备份数据的内容进行一个处理。

在启动redis客户端的时候,使用bgrewriteaof指令,就可以对aof备份文件的内容进行

优化压缩处理。截图对比一下处理先后的大小

redis快照怎么触发 redis快照与aof原理_Redis_13

完毕