本文首发于:https://mp.weixin.qq.com/s/WVUGWuNrGoyY_7aDf7NNmA

微信公众号:后端技术指南针

0.前言

通俗讲持久化就是将内存中的数据写入非易失介质中,比如机械磁盘和SSD。

在服务器发生宕机时,作为内存数据库Redis里的所有数据将会丢失,

因此Redis提供了持久化两大利器:RDB和AOF。

 RDB:Redis Dump Binary #全量二进制导出
 AOF: Append-Only File #增量文件追加
  • RDB 将数据库快照以二进制的方式保存到磁盘中。
  • AOF 以协议文本方式,将所有对数据库进行过写入的命令和参数记录到 AOF 文件,从而记录数据库状态。

1.RDB的配置

  • 查看RDB配置
 [redis@abc]$ cat /abc/redis/conf/redis.conf
 save
 save
 save
 dbfilename "dump.rdb"
 dir "/data/dbs/redis/rdbstro" 

上图为Redis配置文件里默认的RDB设置:

前三行都是对触发RDB的一个条件, 如第一行表示每900秒钟有一条数据被修改则触发RDB,依次类推;只要一条满足就会进行RDB持久化;

第四行dbfilename指定了把内存里的数据库写入本地文件的名称,该文件是进行压缩后的二进制文件;

第五行dir指定了RDB二进制文件存放目录 ;

  • 修改RDB配置

在命令行里进行配置,服务器重启才会生效:

 [redis@abc]$ bin/redis-cli
 > CONFIG GET save
 ) "save"
 ) "900 1 300 10 60 10000"
 > CONFIG SET save "21600 1000"
 OK

2.RDB的SAVE和BGSAVE

RDB文件是很适合数据的容灾备份与恢复,通过RDB文件恢复数据库耗时较短,可以快速恢复数据。

RDB持久化只会周期性的保存数据,在未触发下一次存储时服务宕机,就会丢失增量数据。

当数据量较大的情况下,fork子进程这个操作很消耗cpu,可能会发生长达秒级别的阻塞情况。

SAVE是阻塞式持久化,执行命令时Redis主进程把内存数据写入到RDB文件中直到创建完毕,期间Redis不能处理任何命令。  

BGSAVE属于非阻塞式持久化,创建一个子进程把内存中数据写入RDB文件里,同时主进程处理命令请求。

如图展示了bgsave的简单流程:

3.BGSAVE实现细节

RDB方式的持久化是通过快照实现的,符合条件时Redis会自动将内存数据进行快照并存储在硬盘上,以BGSAVE为例,一次完整数据快照的过程:

  1. Redis使用fork函数创建子进程;
  2. 父进程继续接收并处理命令请求,子进程将内存数据写入临时文件;
  3. 子进程写入所有数据后会用临时文件替换旧RDB文件;

执行fork的时OS会使用写时拷贝策略,对子进程进行快照过程优化。

Redis在进行快照过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是任何时候RDB文件都是完整的。

我们可以通过定时备份RDB文件来实现Redis数据库备份,RDB文件是经过压缩的,占用的空间会小于内存中的数据大小。
除了自动快照还可以手动发送SAVE或BGSAVE命令让Redis执行快照。

通过RDB方式实现持久化,由于RDB保存频率的限制,如果数据很重要则考虑使用AOF方式进行持久化。

4.AOF的配置

在使用AOF持久化方式时,Redis会将每一个收到的写命令都通过Write函数追加到文件中类似于MySQL的binlog。

换言之AOF是通过保存对redis服务端的写命令来记录数据库状态的。AOF文件有自己的存储协议格式:

 [redis@abc]$ more appendonly.aof
 *     # 2个参数
 $     # 第一个参数长度为
 SELECT     # 第一个参数
 $     # 第二参数长度为
      # 第二参数
 *     # 3个参数
 $     # 第一个参数长度为
 SET     # 第一个参数
 $     # 第二参数长度为
 name     # 第二个参数
 $     # 第三个参数长度为
 Jhon     # 第二参数长度为 

AOF配置:

 [redis@abc]$ more ~/redis/conf/redis.conf
 dir "/data/dbs/redis/abcd"           #AOF文件存放目录
 appendonly yes                       #开启AOF持久化,默认关闭
 appendfilename "appendonly.aof"      #AOF文件名称(默认)
 appendfsync no                       #AOF持久化策略
 auto-aof-rewrite-percentage       #触发AOF文件重写的条件(默认)
 auto-aof-rewrite-min-size 64mb       #触发AOF文件重写的条件(默认)

当开启AOF后,服务端每执行一次写操作就会把该条命令追加到一个单独的AOF缓冲区的末尾,然后把AOF缓冲区的内容写入AOF文件里,

由于磁盘缓冲区的存在写入AOF文件之后,并不代表数据已经落盘了,

而何时进行文件同步则是根据配置的appendfsync来进行配置。

appendfsync有三个选项:always、everysec和no:

  1. always:服务器在每执行一个事件就把AOF缓冲区的内容强制性的写入硬盘上的AOF文件里,保证了数据持久化的完整性,效率是最慢的但最安全的;
  2. everysec:服务端每隔一秒才会进行一次文件同步把内存缓冲区里的AOF缓存数据真正写入AOF文件里,兼顾了效率和完整性,极端情况服务器宕机只会丢失一秒内对Redis数据库的写操作;
  3. no:表示默认系统的缓存区写入磁盘的机制,不做程序强制,数据安全性和完整性差一些。

5.AOF重写

  • AOF重写的必要性

AOF比RDB文件更大,并且在存储命令的过程中增长更快,为了压缩AOF的持久化文件,Redis提供了重写机制以此来实现控制AOF文件的增长。

AOF重写实现的理论基础是这样的:

  1. 执行set hello world 50次
  2. 最后执行一次 set hello china
  3. 最终对于AOF文件而言前面50次都是无意义的,AOF重写就是将key只保存最后的状态。
  4. 重写期间的数据一致性问题

子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 会出现数据库的数据和重写后的 AOF 文件中的数据不一致。

因此Redis 增加了一个 AOF 重写缓存, 这个缓存在 fork 出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到现有的 AOF 文件之外, 还会追加到这个缓存中。

  • AOF文件覆盖

当子进程完成 AOF 重写之后向父进程发送一个完成信号, 父进程在接到完成信号之后会调用信号处理函数,完成以下工作:

  1. 将 AOF 重写缓存中的内容全部写入到新 AOF 文件中
  2. 对新的 AOF 文件进行改名,覆盖原有的 AOF 文件
  3. AOF重写的阻塞性

整个 AOF 后台重写过程中只有最后写入缓存和改名操作会造成主进程阻塞, 在其他时候AOF 后台重写都不会对主进程造成阻塞, 将 AOF 重写对性能造成的影响降到了最低。

  • AOF重写的触发条件

AOF 重写可以由用户通过调用 BGREWRITEAOF 手动触发。服务器在 AOF 功能开启的情况下,会维持以下三个变量:

  1. 当前 AOF 文件大小
  2. 最后一次 重写之后, AOF 文件大小的变量
  3. AOF文件大小增长百分比

每次当 serverCron 函数执行时, 它都会检查以下条件是否全部满足, 如果是的话, 就会触发自动的 AOF 重写:

  1. 没有 BGSAVE 命令在进行 防止于RDB的冲突
  2. 没有 BGREWRITEAOF 在进行 防止和手动AOF冲突
  3. 当前 AOF 文件大小至少大于设定值 基本要求 太小没意义
  4. 当前 AOF 文件大小和最后一次 AOF 重写后的大小之间的比率大于等于指定的增长百分比

6.Redis的数据恢复

  1. Redis 恢复优先级
  2. 如果只配置 AOF ,重启时加载 AOF 文件恢复数据;
  3. 如果同时配置了 RDB 和 AOF ,启动只加载 AOF 文件恢复数据;
  4. 如果只配置 RDB,启动将加载 dump 文件恢复数据。
  5. AOF恢复方式

拷贝 AOF 文件到 Redis 的数据目录,启动 redis-server AOF 的数据恢复过程:Redis 虚拟一个客户端,读取AOF文件恢复 Redis 命令和参数,然后执行命令从而恢复数据。这些过程主要在loadAppendOnlyFile() 中实现。

  • RDB恢复方式

拷贝 RDB 文件到 Redis 的数据目录,启动 redis-server即可,因为RDB文件和重启前保存的是真实数据而不是命令状态和参数。

7.新型的混合型持久化

RDB和AOF都有各自的缺点:

  1. RDB是每隔一段时间持久化一次, 故障时就会丢失宕机时刻与上一次持久化之间的数据,无法保证数据完整性
  2. AOF存储的是指令序列, 恢复重放时要花费很长时间并且文件更大

Redis 4.0 提供了更好的混合持久化选项: 创建出一个同时包含 RDB 数据和 AOF 数据的 AOF 文件, 其中 RDB 数据位于 AOF 文件的开头,

它们储存了服务器开始执行重写操作时的数据库状态,至于那些在重写操作执行之后执行的 Redis 命令,

则会继续以 AOF 格式追加到 AOF 文件的末尾, 也即是 RDB 数据之后。

8.持久化实战

在实际使用中需要根据Redis作为主存还是缓存、数据完整性和缺失性的要求、CPU和内存情况等诸多因素来确定适合自己的持久化方案,

一般来说稳妥的做法包括:

  1. 最安全的做法是RDB与AOF同时使用,即使AOF损坏无法修复,还可以用RDB来恢复数据,当然在持久化时对性能也会有影响。
  2. Redis当简单缓存,没有缓存也不会造成缓存雪崩只使用RDB即可。
  3. 不推荐单独使用AOF,因为AOF对于数据的恢复载入比RDB慢,所以使用AOF的时候,最好还是有RDB作为备份。
  4. 采用新版本Redis 4.0的持久化新方案。

理解Redis持久化的更多相关文章

  1. 一篇文章彻底理解Redis持久化:RDB和AOF

    为什么需要持久化? Redis对数据的操作都是基于内存的,当遇到了进程退出.服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复.有了持久化机制,Redis在下次重启时可以 ...

  2. 5分钟彻底理解Redis持久化

    Redis持久化 RDB快照 在默认情况下,Redis将内存数据库快照保存到dump.rdb的二进制文件中. 可以对Redis进行设置,让它在"N秒内数据集至少有N个改动", 这一 ...

  3. 10分钟彻底理解Redis持久化和主从复制

    在这篇文章,我们一起了解 Redis 使用中非常重要的两个机制:Reids 持久化和主从复制. 什么是 Redis 持久化? Redis 作为一个键值对内存数据库(NoSQL),数据都存储在内存当中, ...

  4. 读完这篇,让你真正理解Redis持久化

    什么叫持久化? 用一句话可以将持久化概括为:将数据(如内存中的对象)保存到可永久保存的存储设备中. 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中. XML 数据文件中等等. 也 ...

  5. 深入理解redis持久化

    持久化方式: 快照(RDB)方式,默认方式,文件以二进制方式保存到RDB文件. 文件追加(AOF)方式,文件以协议文本的方式write到AOF文件. 作用,重启后的数据恢复.当两种方式都启用时,red ...

  6. 源码级别理解 Redis 持久化机制

    文章首发于公众号"蘑菇睡不着",欢迎来访~ 前言 大家都知道 Redis 是一个内存数据库,数据都存储在内存中,这也是 Redis 非常快的原因之一.虽然速度提上来了,但是如果数据 ...

  7. 【redis持久化】redis持久化理解

    1.以下内容仅为个人理解和总结,仅供参考,万万不可全盘真信,内容会进行实时改进和修正 2.redis持久化: 参考链接1.https://redis.io/topics/persistence  -- ...

  8. 深入理解Redis的持久化机制和原理

    Redis是一种面向“key-value”类型数据的分布式NoSQL数据库系统,具有高性能.持久存储.适应高并发应用场景等优势.它虽然起步较晚,但发展却十分迅速. 近日,Redis的作者在博客中写到, ...

  9. 【大厂面试06期】谈一谈你对Redis持久化的理解?

    Redis持久化是面试中经常会问到的问题,这里主要通过对以下几个问题进行分析,帮助大家了解Redis持久化的实现原理. 1.Redis持久化是什么? 2.Redis持久化有哪些策略?各自的实现原理是怎 ...

随机推荐

  1. 记录手动签名APK的过程

    记录手动签名APK的过程 前两天更新了华为平台上的APK,被驳回,原因是新APK签名和老的APK不一致,老用户安装会失败,用命令行安装会报如下的错误: harlanc@harlancdeMacBook ...

  2. Python 命令行之旅:使用 docopt 实现 git 命令

    作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...

  3. Leetcode(3)无重复字符的最长子串

    Leetcode(3)无重复字符的最长子串 [题目表述]: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 第一种方法:暴力 执行用时:996 ms: 内存消耗:12.9MB 效果: ...

  4. java中多线程 - 如何创建多线程

    线程 什么是线程: 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源 表面上 ...

  5. Java源码 Integer.bitCount实现过程

    public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x55555555); ...

  6. Shell进阶精品课程

    课程链接 Shell精品进阶教程:理解Shell的方方面面 课程目标 系统性的掌握shell相关知识,进阶shell脚本能力,对shell各方面了然于心 适用人群 具备shell基础但想深入.系统性掌 ...

  7. Ubuntu编译安装nginx以及配置自动启动

    本文主要介绍ubuntu如何编译安装nginx以及遇到的问题 和 配置系统自动启动服务 查看操作系统版本 cat /etc/issue  Ubuntu 18.04.3 LTS \n \l    更改镜 ...

  8. Spring之Zuul初步使用(十)

    一.zuul是什么 zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用. Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架. ...

  9. python:__name__的使用

    1.python中__name__是什么意思? 很多python的初学者可能都会遇到一个问题:__name__是什么意思? 在刚开始学习python的时候,我们总会看到这样一句代码: if __nam ...

  10. RocketMQ4.2 最佳实践之集群搭建

    学习了RocketMQ的基本概念后,我们来看看RocketMQ最简单的使用场景.RocketMQ的服务器最简单的结构,必须包含一个NameServer和一个Broker.Producer把某个主题的消 ...