文章目录
  1. 1. RDB快照原理
    1. 1.1. AOF 原理
    2. 1.2. 混合持久化
    3. 1.3. RDB优势
    4. 1.4. RDB劣势
  2. 2. AOF优势
    1. 2.1. AOF劣势
    2. 2.2. 常用配置
    3. 2.3. 过期策略
    4. 2.4. 总结

redis提供两种方式进行持久化,一种是RDB快照持久化(原理是将Reids在内存中的数据库记录压缩后定时dump到磁盘上的RDB持久化,存储紧凑),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件), AOF 日志在长期的运行过程中会变的无比庞大,数据库重启时需要加载AOF日志进行指令重放,这个时间就会无比漫长。所以需要定期进行AOF重写,给AOF日志进行瘦身。

RDB快照原理

RDB(快照/内存快照),就是redis按照一定的时间周期将目前服务中的所有数据全部写入到磁盘中。
我们知道redis是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写。

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

redis使用操作系统的多进程COW(Copy On Write) 机制来实现快照持久化。

redis 在持久化时会fork一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。

这个时候就会使用操作系统的COW机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。

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

1
2
3
4
5
6
0.假设现在redis数据存储在内存的A区;
1.此时因配置或某种原因触发了RDB快照事件;
2.触发RDB快照事件后,父进程会先fork出一个子进程, 把处理RDB快照的事情完全交给这个子进程处理,而父进程则继续处理来自客服端的请求;
3.子进程会先将当前内存A区数据压缩, 然后dump刷盘到一个临时RDB文件中, 当dump完成后,再将这个临时RDB文件替换之前的RDB文件, 然后子进程结束退出;
4.同样,在子进程处理快照dump过程中, 如果父进程接收到新的客服端请求,则父进程需要先拷贝一份内存A区中相关数据页的信息到内存B区,然后在B区上完成客服端的请求;
5.当父进程完成新的客服端请求后,发现子进程已经完成了RDB快照处理, 则将刚才更新的B区数据取替换A区数据, 如果子进程还没有完成则等待;

AOF 原理

AOF日志存储的是redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录(查询/删除指令是不记录的)
假设 AOF 日志记录了自redis实例创建以来所有的修改性指令序列,那么就可以通过对一个空的redis 实例顺序执行所有的指令,也就是「重放」,来恢复redis当前实例的内存数据结构的状态。

redis会在收到客户端修改指令后,进行参数校验进行逻辑处理后,如果没问题,就立即将该指令文本存储到AOF日志中,也就是先执行指令才将日志存盘。这点不同于leveldb、hbase等存储引擎,它们都是先存储日志再做逻辑处理。

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

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

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

这就意味着如果机器突然宕机,AOF 日志内容可能还没有来得及完全刷到磁盘中,这个时候就会出现日志丢失。那该怎么办? 可以通过开启fsync配置来强制同步刷盘, 过于频繁的fsync会严重拖慢redis性能,所以在生产环境的服务器中,redis 通常是每隔1s左右执行一次fsync操作, 在保持高性能的同时,尽可能使得数据少丢失。

混合持久化

重启 redis 时,我们很少使用 RDB 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB 来说要慢很多,这样在redis 实例很大的情况下,启动需要花费很长的时间。

redis4.0为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 RDB 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。

于是在redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

RDB优势

一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB劣势

如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF优势

该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF劣势

对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

常用配置

RDB持久化配置
Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

1
2
3
4
5
save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

AOF持久化配置
在Redis的配置文件中存在三种同步方式,它们分别是:

1
2
3
4
5
appendfsync always     #每次有数据修改发生时都会写入AOF文件。

appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。

appendfsync no #从不同步。高效但是数据不会被持久化。

过期策略

RDB过期key处理策略

已过期的键不会被保存到新创建的RDB文件中。举个例子,如果数据库中包含三个键k1、k2、k3,并且k2已经过期,那么当执行SAVE命令或者BGSAVE命令时,程序只会将k1和k3的数据保存到RDB文件中,而k2则会被忽略。因此,数据库中包含过期键不会对生成新的RDB文件造成影响。

在启动Redis服务器时,如果服务器开启了RDB功能,那么服务器将对RDB文件进行载入:

如果服务器以主服务器模式运行,那么在载入RDB文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载入RDB文件的主服务器不会造成影响;

如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有键,不论是否过期,都会被载入到数据库中。不过,因为主从服务器在进行数据同步的时候,从服务器的数据库就会被清空,所以一般来讲,过期键对载入RDB文件的从服务器也不会造成影响;

AOF过期key处理策略

当服务器以AOF持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期键而产生任何影响。当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加(append)一条DEL命令,来显式地记录该键已被删除。

和生成RDB文件时类似,在执行AOF重写的过程中,程序会对数据库中的键进行检查,已过期的键不会被保存到重写后的AOF文件中。举个例子,如果数据库中包含三个键k1、k2、k3,并且k2已经过期,那么在进行重写工作时,程序只会对k1和k3进行重写,而k2则会被忽略。

总结
  • redis数据存储持久化机制上, 探讨了RDB快照AOF两种持久化方案,对其原理,区别等进行了进一步的说明;
  • 进一步探讨了redis.4.0提供的混合持久化方案;
  • 归类总结了RDBAOF两种方案的优缺点;
  • 从经验出发, 进一步总结了实际中常用的配置方案;
  • 进一步总结了RDBAOF对过期key的处理策略;

作者署名:朴实的一线攻城狮
本文标题:redis专题13 数据存储与持久化
本文出处:http://researchlab.github.io/2018/10/10/redis-13-data-storage-solution/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。

@全栈炼狱之路

关注微信公众号 @全栈炼狱之路

总访问:
总访客: