redis持久化策略【面试必看】

持久化

内存中的数据是不持久的,要想做到持久,就需要把redis中的数据存储到硬盘

redis插入数据:需要把这个数据同时写入到内存和硬盘,写入是有策略的

redis读取数据:当查询某个数据的时候,直接从内存中读取

硬盘的数据只是在redis重启的时候,用来恢复内存中的数据

代价:消耗了更多的空间,同一份数据,存储了两遍

redis实现持久化的策略

RDB(定期备份)

RDB定期把我们redis内存中的所有数据,都写入硬盘中,生成一个快照,后续redis一旦重启了,(内存中的数据没有了),就可以根据刚才的快照,把内存中的数据给恢复回来

定期有两种策略:手动触发和自动触发

手动触发

save

程序员通过redis客户端,执行特定的命令,来触发快照的生成。执行save的时候,redis就会生成快照操作,此时就会阻塞redis的其他客户端的命令,导致类似于key*的后果,所以不太建议使用save

bgsave

bg→background,不会影响redis服务器处理其他客户端的请求和命令,此处redis通过多进程的方式来完成的并发编程,具体原理如下:

在这里插入图片描述

  1. 执⾏ bgsave 命令,Redis ⽗进程判断当前进是否存在其他正在执⾏的⼦进程,如 RDB/AOF ⼦进
    程,如果存在 bgsave 命令直接返回。
  2. ⽗进程执⾏ fork 创建⼦进程,fork 过程中⽗进程会阻塞,通过 info stats 命令查看
    latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。
  3. ⽗进程 fork 完成后,bgsave 命令返回 “Background saving started” 信息并不再阻塞⽗进程,可以继续响应其他命令。
  4. ⼦进程创建 RDB ⽂件,根据⽗进程内存⽣成临时快照⽂件,完成后对原有⽂件进⾏原⼦替换。执⾏ lastsave 命令可以获取最后⼀次⽣成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选
    项。
  5. 进程发送信号给⽗进程表⽰完成,⽗进程更新统计信息。

redis生成的rdb文件是存放在redis的工作目录中的

配置文件中有rdb的配置路径,cd /etc/redis/redis.conf,rdb文件的路径为:/var/lib/redis,默认rdb生成持久化的名字为dump.rdb,它是一个二进制文件,把内存中的数据以压缩的形式保存到这个二进制文件中(压缩的形式,会消耗一定的CPU资源,但是能节省存储空间),自己不要修改,会出错,后续redis服务器重新启动,就会尝试加载这个rdb文件。如果发现格式错误,就可能会加载数据失败

rdb文件即使我们不主动去修改它,它也可能会出错(主从复制),例如网络传输会有问题,导致这个文件被破坏

redis提供了rdb文件检查工具→redis-check-rdb*

rdb持久化操作可以触发多次,当执行生成rdb镜像操作的时候,此时就要把生成的快照数据,先保存到一个临时文件中,当这个快照生成完毕之后,再删除之间的rdb文件,把新生成的临时的rdb文件名字改成刚刚的dump.rdb

rdb文件中的数据,不是插入了数据,就会立即更新

rdb的触发时机:

1.手动(save,bgsave)

2.自动(配置文件中,进行设置)

自动触发

在这里插入图片描述

虽然此处的这些值可以随便修改,但是修改上述数据的时候有一个原则:生成一个rdb快照,这个成本比较高,不能让这个操作执行的太频繁了,正因为rdb生成的操作不能太频繁,这就导致,快照里的数据和当前实时的数据会存在偏差,例如save 60 10000,两次生成rdb之间的间隔,最少是60秒这就导致如果在60秒内服务器挂了,这些数据就全部丢了

实际操作

1.手动执行save&bgsave触发生成一次快照

在这里插入图片描述

插入几个key之后,我们使用bgsave,bgsave瞬间完成,因为这里的数据比较少,在redis服务器重启的后,会加载rdb文件的内容,恢复之前在内存中的状态

2.插入新的key,不手动执行bgsave

我们插入少量的key,让它达不到自动触发的值,使用service redis-server restart,重启以后我们会发现key依旧存在

在这里插入图片描述

但是如果使用kill命令去杀死就不存在

结论:如果是通过正常流畅重启redis服务器,此时redis服务器会在退出的时候,自动触发生成rdb的操作,但是如果是异常重启(kill -9或者服务器掉电等)此时redis服务器来不及生成rdb,导致数据丢失

所以redis生成快照的操作,不仅有手动触发,也可以自动触发,自动触发的几种情况

1.通过配置文件中save执行M时间内,修改N次
2.通过shutdown命令(redis里的一个命令),或者service redis-server restart命令关闭服务器也会触发
3.redis进行主从复制的时候,主节点也会自动生成rdb快照,然后把rdb快照文件内容传输给从节点(后面介绍)

3.bgsave操作流程是创建子进程,子进程完成持久化操作(难以观察到子进程,因为执行速度太快),持久化会把数据写入到新的文件中,然后用新的文件替换旧的文件(可以观察到)

可以使用stat命令,查看文件的inode编号

在这里插入图片描述

在这里插入图片描述

重点:如果直接使用save命令,此时是不会触发子进程和文件替换逻辑,如果是sav就直接在当前进程中,往刚才的同一个文件中写入数据了

Linux文件系统 文件系统典型的组织方式(ext4)主要把整个文件系统分成了三大部分
1.超级快(存放一些管理信息)
2.inode区(存放inode节点,每个节点都会分配一个inode数据结构,包含了文件的各种元数据)
3.block(存放文件的内容数据)

4.通过配置自动生成rdb快照

利用如下条件,再去看dump.rdb,会自动更改

在这里插入图片描述

执行flushall也会清空rdb文件

配置文件修改之后,一定要重新启动服务器,才能生效,如果想立即生效,也可以通过命令的方式修改

5.把rdb文件,故意改坏了,会如何?

手动的把rdb文件内容改坏,一定是通过kill进程的方式(如果不是kill的话,它会保存正确的结果,修改不会真正生效),然后重启redis服务器,不过redis服务器有可能看起来没受到什么影响,还是能正确获取到key,这里redis会怎么样,取决于rdb文件坏在哪里,如果改坏的地方是文件末尾,对前面的内容没什么影响,但是如果更改的是中间,那redis服务器可能就重启不了了

redis服务器挂了的时候,可以看看redis日志,在/var/log/redis路径下,查看下面的redis-server.log即可

rdb文件是二进制的,直接就把坏的rdb文件交给redis服务器去使用,得到的结果是不可预期的,可能redis服务器能启动,但得到的数据也可能有问题,也可能redis服务器直接启动失败

redis提供了rdb文件检查工具→redis-check-rdb

在这里插入图片描述

rdb的优缺点

  • RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中(如 hdfs)⽤于灾备。
  • Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。(RDB使用的二进制方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中,AOF是使用文本的方式组织数据,需要一系列的字节串切分操作)
  • RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。
  • RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛
    (rdb文件新老版本不兼容,我们可以写一个程序,直接遍历旧的redis中所有的key,把数据取出来放到新版本的redis中)。

AOF(定时备份)

类似于mysql的bin log,会把用户的每个操作都记录到文件中。当redis重新启动的时候,就会读取aof文件中的内容,用来恢复数据。当开启aof的时候,rdb就不生效了,启动的时候不再读取rdb文件内容了

aof默认不开启,我们需要自行设置,在/etc/redis,在redis.conf里面更改,把appendonly no改为appendonly yes

在这里插入图片描述

所在的目录和rdb一样,/var/lib/redis,插入数据以后,查看appendonly.aof

在这里插入图片描述

可以看出AOF是一个文本文件,每次的操作都会被记录到文本文件中,通过一些特殊符号作为分隔符,来对命令的细节做区分(分隔符的规则,不要研究)

引入AOF之后,又要写内存,又要写硬盘,还能和之前一样快吗?

AOF工作流程

在这里插入图片描述

  1. 所有的写⼊命令会追加到 aof_buf(缓冲区)中。
  2. AOF 缓冲区根据对应的策略向硬盘做同步操作。
  3. 随着 AOF ⽂件越来越⼤,需要定期对 AOF ⽂件进⾏重写,达到压缩的⽬的。
  4. 当 Redis 服务器启动时,可以加载 AOF ⽂件进⾏数据恢复。

所以实际上没有影响,原因如下:

  1. AOF机制并非直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区,积累一波之后,再统一写入硬盘
  2. AOF是每次把新的操作写入到原有文件末尾,属于顺序写入

如果把数据写入到缓冲区里,本质还是在内存,这个时候进程挂了或者主机掉电了,缓冲区来不及写入硬盘,数据会丢失

redis给出了一些选项,在/ect/redis/redis.conf

在这里插入图片描述

系统调⽤ write 和 fsync 说明:

  1. write 操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性
    能。write 操作在写⼊系统缓冲区后⽴即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区⻚空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
  2. Fsync 针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。
  3. 配置为 always 时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。
  4. 配置为 no 时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据重要程度很低,⼀般不建议配置。
  5. 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1秒的数据。

结论:刷新频率越高,性能就影响越大,同时数据的可靠性越高,否则反之

AOF文件持续增长,体积会越来越大,会影响到redis下次启动的启动时间,因为aof文件中,有一些内容是冗余的,例如:

在这里插入图片描述

因此redis就存在一个机制,能够针对aof文件进行整理操作,这个整理能够去除其中的冗余操作,并且合并一些操作,达到给aof文件瘦身的效果,重写的原理:根据内存中最终数据状态重写

重写机制

分为手动触发和自动触发

  • ⼿动触发:调⽤ bgrewriteaof 命令。
  • ⾃动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时机。
    auto-aof-rewrite-min-size:表示触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。
    auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。

AOF重写流程

在这里插入图片描述

  1. 执⾏ AOF 重写请求。
    如果当前进程正在执⾏ AOF 重写,请求不执⾏。如果当前进程正在执⾏ bgsave 操作,重写命令
    延迟到 bgsave 完成之后再执⾏。
  2. ⽗进程执⾏ fork 创建⼦进程。
  3. 重写

子进程写新的aof文件的同时,父进程仍然在不停的接收客户端的新的请求。父进程还是会写把这些请求产生的 AOF 数据先写入到缓冲区再刷新到原有的 AOF 文件里,在创建子进程的一瞬间,子进程就继承了当前父进程的内存状态。因此,子进程里的内存数据是 父进程 fork 之前的状态。fork 之后,新来的请求,对内存造成的修改,是子进程不知道的,此时,父进程这里又准备了一个 aof rewrite buf缓冲区,专门放fork 之后收到的数据,子进程这边,把aof数据写完之后,会通过 信号 通知一下父进程,父进程再把aof rewrite buf 缓冲区中的内容也写入到新 AOF文件里,就可以用新的 AOF 文件代替1日的 AOF 文件了

疑问:父进程fork完毕之后,就已经让子进程写新的aof文件了,并且随着时间的推移,子进程很快就写完了新的文件,要让新的aof文件代替旧的,父进程此时还在继续写这个即将消亡的旧的aof文件是否还有意义?
答:极端情况下,重写过程中,重写了一半,服务器挂了,子进程内存中的数据就会丢失,新的aof文件内容还不完整,所以父进程不坚持写旧的aof文件,重启就没办法保证数据的完整性

混合持久化

在/ect/redis/redis.conf,aof-use-rdb-preamble字段来控制是否是混合持久化,默认是yes

aof本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续的加载成本是比较高的,redis就引入了“混合持久化的方式”,结合了rdb和aof的特点,按照aof的方式,每一个请求/操作,都记录入文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中,后续在进行的操作,仍然是按照aof文本的方式追加到文件后面

aof和rdb

当redis上同时存在aof文件和rdb快照的时候,此时以aof为主,因为aof中包含的数据比rdb更安全

在这里插入图片描述

总结

  1. Redis 提供了两种持久化⽅案:RDB 和 AOF。
  2. RDB 视为内存的快照,产⽣的内容更为紧凑,占⽤空间较⼩,恢复时速度更快。但产⽣ RDB 的开销较⼤,不适合进⾏实时持久化,⼀般⽤于冷备和主从复制。
  3. AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件
  4. RDB 和 AOF 都使⽤ fork 创建⼦进程,利⽤ Linux ⼦进程拥有⽗进程内存快照的特点进⾏持久化,尽可能不影响主进程继续处理后续命令。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值