Redis之持久化

引言

即使Redis的进程不小心关闭或者宕机也问题不大,redis虽然是内存数据库,但是会将缓存数据保存到硬盘,一旦关闭后,可以通过AOF RDB持久化重新加载到数据。

AOF持久化怎么实现的?

基本介绍

AOF(Append Only File):redis每执行一条写操作命令,就把该命令以追加的方式写入到文件里。重启redis的时候,先去读取该文件的命令,并且执行,就相当于回复了缓存数据。(只记录写,不记录读 记录读没意义)
在这里插入图片描述
AOF文件内容
在这里插入图片描述
*3----> 命令有三部分 每部分都以$+数字开头 后面紧跟命令、键、值
$3 set 命令有三个字节
Redis先执行写操作,再将命令记录到AOF

  • 优点1:避免额外的检查开销 如果先记录,再执行 当命令语法有问题,错误的命令就会被放到AOF里,导致恢复数据出错 先执行再记录 只有执行成功才会记录 保证AOF的命令都是正确且已经被执行的
  • 优点2:不会阻塞当前写操作的执行 不同时进行 不会造成阻塞
  • 风险1:执行和记录先后进行,存在这种情况,执行之后服务器宕机 这条日志未被记录 有丢失风险
  • 风险2:命令1 执行–>记录1,命令2 因为写入命令也是在主进程进行的 可能会阻塞下一个命令
  • 在这里插入图片描述
    (这两个风险都跟AOF日志写回硬盘的时机有关,下面讲三种写回策略)

三种写回策略

执行命令后的写操作的步骤

  • 将命令追加到server.aof_buf缓冲区
  • 通过write()系统调用 将aof_buf缓冲区的数据写入到AOF文件,此时并没写入硬盘,而是拷贝到page cache 等待内核数据写入硬盘
  • 具体内核缓冲区数据何时写入硬盘 由内核决定
    写回策略有三种
  • Always 总是 :执行完命令后总是立刻写回(高可靠 数据丢失少
    • 最大程度保证数据不丢失 但不可避免回影响主进程性能
  • Everysec 每秒 :执行完命令,在内核缓冲区每隔一秒将缓冲区内容写回(可以接受丢失一点,但又想高性能 折中办法
    • 折中的方式,避免了Always的性能开销,也比NO能避免数据丢失 但如果上一秒写入的日志没写回就宕机 还是回丢失
  • NO 写回时机不由Redis控制 交给操作系统决定(高性能 性能不易阻塞
    • 比Always的性能好,但是写回时机不可预知,如果内写回,一旦宕机回丢失不少数据 但如果不宕机 性能比较高
      (三种方法都无法解决 主进程阻塞和减少数据丢失问题)
      在这里插入图片描述
      三种策略的实现很简单,只是在控制fsync()的时机 一个立即执行 一个创建异步任务执行 一个永不执行

AOF重写机制

随着写操作越来越多,AOF也会越来越大,重启后的数据可能要恢复很久。为避免过大,提供了重写机制。

  • 重写是读取数据库所有键值对,对每个键值对用一条命令记录到【新的AOF文件】整理完之后再替换掉原来的文件(如果是之间覆盖,那么一旦重写过程失败 就会污染原有AOF文件)
    举例:对 name 1 执行了 set name 1; set name 2:…set name n 重写后就之间成为一条 set name n 这一条就能覆盖之前的命令
    在这里插入图片描述
  • 重写之后大大减少了命令的数量,相当于压缩了AOF文件。
  • 重写不是在原有AOF文件修改,而是重新写到新的AOF文件,再去覆盖原有文件。

AOF后台重写

重写AOF过程是有后台子进程来完成的。

  • 子进程在重写期间,主进程可以继续处理命令请求,避免阻塞主进程。
  • 子进程和主进程共享内存数据,不过这里是只读的方式,一旦父子进程一方修改了该共享内存,就会出现【写时复制】。两者都有了独立的数据副本,不用加锁保证数据安全。比用线程好(线程共享内存,修改时会加锁,降低性能。)
    子进程如何获得数据副本?
  • 父进程调用生成子进程的时候,操作系统把页表复制一份给子进程,页表记录虚拟地址和物理地址的映射关系,不会复制物理内存(性能高)。两者虚拟空间不一样,但物理空间一样。
  • 当父或子修改是们才会物理内存复制(有必要的时候才复制)(复制的是主进程修改的物理内存,没修改的还是共享 2w条数据 300条被复制了 总比2w全都被复制性能高)
  • 在这里插入图片描述
    在这里插入图片描述
  • 【写时复制】优点;防止fork子进程时,由于物理内存数据复制时间过长导致父进程阻塞。
    两个阶段会阻塞父进程:
  • 创建子进程的时候,复制页表会阻塞进程。
  • 写时复制,需要拷贝物理内存,会阻塞。
    重写AOF文件的时候,主进程继续处理,这时两者不一致怎么办?
  • 【AOF重写缓冲区】这个缓冲区在创建子进程后开始启用
  • 重写AOF期间,redis执行完一个命令,会同时写到【缓冲区】和【重写缓冲区】
    在这里插入图片描述
    子进程完成重写后(扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志),向进程发一条信号,异步的。
    主进程收到信号后:
  • 将【AOF重写缓冲区】的内容追加到【新的AOF】中 保证一致
  • 将新的AOF文件改名,覆盖到原有AOF

RDB快照是怎么实现的?

快照,就是记录某一个时刻的全部画面。
RDB快照就是记录某个瞬间内存的实际数据(AOF记录的是命令数据,还得重新执行才能恢复原数据。)直接读入内存就可以,恢复数据效率高。

快照怎么用?

  • save 在主线程生成RDB,如果时间长,会阻塞主线程。
  • bgsave 创建子进程生成RDB 避免主线程的阻塞
  • 默认配置 save 900 1(900 秒之内,对数据库进行了至少 1 次修改;) save 300 10(300 秒之内,对数据库进行了至少 10 次修改;)save 60 10000(60 秒之内,对数据库进行了至少 10000 次修改)
  • 每次执行快照其实就是把内存中所有数据读到磁盘中,这是个比较重的操作。频率太频繁影响性能,频率低,服务器故障时,丢失数据更多
  • 通常五分钟保存一次,服务器宕机最多丢失五分钟数据

AOF 恢复的慢 但是 生成文件快
RDB 恢复的快 但是 快照一次 比较重

执行快照时,数据能被修改吗?

  • 可以的。一样用到【写时复制】(极端情况下 所有共享内存都被修改,那么内存占用才是原来两倍)
  • 如果主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A’),然后主线程在这个数据副本(键值对 A’)进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件
  • 如果恰好在RDB创建后崩溃了系统,那么这个过程中修改的数据会丢失

RDB+AOF

快照的频率不好把握:

  • 频率太低,快照间一旦宕机,较多数据丢失
  • 频率太高,带来性能开销
    【混合持久化】
  • 开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
  • AOF = RDB(保证加载数据快)+AOF(增量命令 保证过程中的数据不丢失)
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值