likes
comments
collection
share

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

作者站长头像
站长
· 阅读数 19

最开始的Redis版本,Redis有两种持久化方式:RDB和AOF,Redis默认采用的是RDB的方式。

自Redis 4.0以后,Redis拥有以下三种持久化方式:

  • 快照方式(RDB, Redis DataBase)将某一个时刻的内存数据,以二进制的方式写入磁盘;
  • 文件追加方式(AOF, Append Only File),记录所有的操作命令,并以文本的形式追加到文件中;
  • 混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能简单数据丢失的风险。

因为每种持久化方案,都有特定的使用场景,让我们先从 RDB 持久化开始讲起吧:

RDB(Redis DataBase)

在主从复制中,RDB就是备用了!从机上面备份,不会影响主机的性能!

面试和工作,持久化都是重点!

Redis是内存数据库,如果不将内存中的数据库状保存到磁盘,那么一旦服务器退出,服务器中的数据状态也会消失。所以Redis提供了持久化功能!

介绍

什么是RDB

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

RDB(Redis DataBase)是将某一个时刻的内存快照(Snapshot),在指定的时间间隔内以二进制的方式写入磁盘的过程。它恢复时是将快照文件直接读到内存里。

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效;RDB的缺点是最后一次持久化后数据可能丢失!我们默认的就是RDB,一般不需要修改配置。

有时候在生产环境我们会将dump.rdb文件进行备份,RDB保存的文件是dump.rdb,是在配置文件中的快照部分进行配置的。

开启RDB

redis.conf文件中找到 SNAPSHOTTING(快照,即RDB的同步方式) ,修改db文件名save同步时间参数即可更改RDB同步配置,RDB两种手动同步机制3种自动同步机制

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

备份就会自动生成一个dump.rdb

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

触发机制

1、save的规则满足的情况下,会自动触发RDB规则

2、执行flushall命令,也会触发我们的RDB规则! 3、退出Redis,也会产生 RDB 文件

如何恢复RDB文件

1、只需要将rdb文件放在我们的Redis启动目录就可以,Redis启动的时候会自动检查dump.rdb恢复其中的数据!

2、查看需要存放的位置

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin/redis"   # 如果在这个目录存在   dump.rdb 文件,启动就会自动恢复其中的数据

几乎redis自己默认的配置就够用了,但是我们还是需要去学习!

优点

1、适合大规模的数据恢复;

2、对数据的完整性不高。

缺点

1、需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改就没有了;

2、fork进程的时候,会占用一定的内存空间。

持久化的两种方式

RDB 的持久化触发方式有两类:一类是手动触发,另一类是自动触发

手动触发

① save(客户端执行)

在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用

② bgsave(异步、非阻塞)

原理:fork() + copy on write

bgsave(background save)既后台保存的意思, 它和 save 命令最大的区别就是 bgsave 会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了,相对于整个流程都阻塞的 save 命令来说,显然 bgsave 命令更适合我们使用。

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

优点

他可以一边进行持久化一边对外提供读写服务,互不影响,新写的数据对我持久化不会造成数据影响,你持久化的过程中报错或者耗时太久都对我当前对外提供请求的服务不会产生任何影响。持久化完会将新的rdb文件覆盖之前的。

自动触发

① save m n(配置文件中修改)

save m n 是指在 m 秒内,如果有 n 个键发生改变,则自动触发持久化。 参数 m 和 n 可以在 Redis 的配置文件中找到,例如,save 60 1 则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化。 自动触发持久化,本质是 Redis 通过判断,如果满足设置的触发条件,自动执行一次 bgsave 命令。 注意:当设置多个 save m n 命令时,满足任意一个条件都会触发持久化。 例如,我们设置了以下两个 save m n 命令:

  • save 60 10
  • save 600 1

当 60s 内如果有 10 次 Redis 键值发生改变,就会触发持久化;如果 60s 内 Redis 的键值改变次数少于 10 次,那么 Redis 就会判断 600s 内,Redis 的键值是否至少被修改了一次,如果满足则会触发持久化。

② flushall

flushall 命令用于清空 Redis 数据库,在生产环境下一定慎用,当 Redis 执行了 flushall 命令之后,则会触发自动持久化,把 RDB 文件清空。 执行结果如下图所示: 【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

③ 主从同步触发(slaveof ip port)

在 Redis 主从复制中,当从节点执行全量复制操作时,主节点会执行 bgsave 命令,并将 RDB 文件发送给从节点,该过程会自动触发 Redis 持久化。

面试常考点

fork()

fork()是Unix和Linux这种操作系统的一个api,而不是Redis的api。fork是系统调用api,copy on write是内核机制。fork()用于创建一个子进程,注意是子进程,不是子线程。fork()出来的进程共享其父类的内存数据。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见

比如:A进程fork()了一个子进程B,那么A进程就称之为主进程,这时候主进程子进程所指向的内存空间是同一个,所以他们的数据一致。但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。这一点有点像守护线程的概念。Redis正是巧妙的运用了fork()这个牛逼的api来完成RDB的持久化操作。

写时复制(copy on write)

主进程fork()子进程之后,内核把主进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向主进程。这也就是共享了主进程的内存,当其中某个进程写内存时(这里肯定是主进程写,因为子进程只负责rdb文件持久化工作,不参与客户端的请求),CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。

中断例程中,内核就会把触发的异常的页复制一份(这里仅仅复制异常页,也就是所修改的那个数据页,而不是内存中的全部数据),于是主子进程各自持有独立的一份。

数据修改之前的样子:

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

数据修改之后的样子:

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

注意

1、在fork子进程的时候,只拷贝指针,并不发生内存的复制。 2、只有当其中的某一个进程试图对该区域进行写操作时,内核就会在物理存储器中为子进程开辟一个新的物理页面,将需要写的区域将父进程的内容复制一份给子进程,然后对新的物理页面进行写操作。

3、调用fork()也会阻塞啊

我只能说没毛病,但是这个阻塞真的可以忽略不计。尤其是相对于阻塞主线程的save。

4、会同时存在多个子进程吗?

不会,主进程每次收到bgsave命令需要fork()子进程之前都会判断是否存在子进程了,若存在也会忽略掉这次bgsave请求。若不存在我会fork()出子进程进行工作。

5、为什么 bgsave 不使用多个子线程 ?

① 如果支持并行存在多个子进程,那么不仅会拉低服务器性能,还会造成数据问题,比如八点的bgsave在工作,九点又来个bgsave命令。这时候九点的先执行完了,八点的后执行完了,那九点的不白执行了吗?这是所谓的数据时序问题。再比如,都没执行完,十点又开一个bgsave,越积越多,服务器性能被拉低。

② 那为什么不阻塞?判断有子进程在工作,就等待,等他执行完我在上场,那一样,越积越多,文件过大,只会造成数据堆积

6、bgsave如果没有copyonwrite这种技术是什么效果?

有效内存空间减半。假设是全量复制,那么内存空间直接减半,浪费资源不说,数据量10g,全量复制这10g的时间也够长的。这谁顶得住?

写的值无法马上更新。如果不全量复制,会是怎样?相当于我一边复制,你一边写数据,看着貌似问题不大,其实不然。比如现在Redis里有k1的值是1,k2的值是2,比如bgsave了,这时候rdb写入了k1的值,在写k2的值之前时,有个客户端请求

set k1 11 
set k2 22

那么持久化进去的是k2 22,但是k1的值还是1,而不是最新的11,所以会造成数据的==脏读==问题,所以采取了copyonwrite技术来保证触发bgsave请求的时候无论你怎么更改,都对我rdb文件的数据持久化不会造成任何影响。

总结

copyonwrite fork()出来的子进程共享主进程的物理(内存)空间,当主子进程有内存写入操作时,read-only内存页发生中断,将触发的异常的内存页复制一份(其余的页还是共享主进程的)。

在 Redis 服务中,子进程只会读取共享内存中的数据,它并不会执行任何写操作,只有主进程会在写入时才会触发这一机制。而对于大多数的 Redis 服务或者数据库,写请求往往都是远小于读请求的,所以使用fork()加上写时拷贝这一机制能够带来非常好的性能,也让bgsave这一操作的实现变得很简单。

AOF(Append Only File)

将我们的所有命令都记录下来,history,恢复的时候就把这个文件再执行一遍!

介绍

是什么

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

日志(协议文本)的形式来记录每个写操作,将Redis执行过程中的所有指令记录下来(读操作不记录),只许追加文件但不可改写文件,redis启动之初会读取该文件重新构建数据。换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

AOF保存的是appendonly.aof文件

append(追加)

append

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

默认是不开启的,我们需要手动进行配置,!只需要改为yes就开启了aof!

AOF重写机制

重写规则说明

aof默认就是文件的无限追加,文件会越来越大!

no-appendfsync-on-rewrite no    # 是否进行重写,默认是no
# rewrite feature.    # 重写特征(配置)auto-aof-rewrite-percentage 100 # 重写百分比
auto-aof-rewrite-min-size 64mb  # 重写最小空间

如果aof文件大于64m,太大了!就会fork一个新的进程来将我们的文件进行重写

重启,redis就可以生效了!

如果这个aof文件有错误,这时候 redis 是启动不起来的我们需要修复这个aof文件

redis给我们提供一个工具 redis-check-aof --fix,rdb没有这个功能。

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

但是发现aof最后一个错误的命令被剔除了!

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

如果文件正常,重启就可以直接恢复了!

【进阶篇】Redis深入理解与实践指南(九)之Redis持久化之RDB与AOF

任何恢复都会有一些允许错误的范围,因此使用redis-check-aof恢复时,会将错误的命令直接剔除掉导致恢复不全面!

三种AOF同步方式

优点和缺点

appendonly no   # 默认是不开启aof模式的,在大部分的情况下,rdb完全够用了!
appendfilename "appendonly.aof" # 持久化文件的名字# appendfsync always    # 没修改都会 sync,消耗性能
appendfsync everysec    # 每秒执行一次 sync,可能会丢失这1s的数据
# appendfsync no        # 不执行 sync,这个时候操作系统自己同步数据,速度最快!# rewrite 重写

优点

1、每一次修改都同步,文件的完整会更加好!

2、每秒同步一次,可能会丢失一秒的数据

3、从不同步,效率最高的!

缺点:

1、相对于数据文件来说,aof远大于rdb,修复的速度也比rdb慢!

2、aof运行效率也rdb慢,所以我们redis默认的配置就是rdb持久化!

因此好好利用AOF的重写机制

AOF的工作原理是将写操作追加到文件中,文件的冗余内容会越来越多。所以聪明的 Redis 新增了重写机制。当AOF文件的大小超过所设定的阈值时,Redis就会对AOF文件的内容压缩。

持久化使用建议

下面是真实使用的建议(精简版):

  1. 若只打算用Redis 做缓存,可以关闭持久化。
  2. 若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB(实际上redis 4.0以后)。

扩展内容

1、RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储

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

3、制作缓存,如果你只希望你的数据在服务器上运行的时候存在,你也可以不适用任何持久化

4、同时开启两种持久化方式

  • 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  • RDB的数据不实时,同时使用时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。

5、性能建议

  • 因为RDB文件只用作后备用途,建议只在Slave上持久化文件而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
  • 如果Enable AOF,好处是在恶劣的情况下也只会丢失不超过两秒数据。启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率。AOF重写的基础大小默认值64M太小了,可以设置到5G以上,默认超过原大小100%重写可以改到适合的数值。
  • 如果不Enable AOF,仅靠Master-Slave Replication(主从复制)实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时宕掉,会丢失10几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个,微博就是这种架构。

下一篇,是有关分布式架构中Redis的主从模式部分的内容了:

欢迎点赞关注评论,感谢观看ヾ(◍°∇°◍)ノ゙