Redis(Remote Dictionary Server ),即长途字典服务,是一个开源的内存高速缓存数据存储服务。运用 ANSI C 言语编写,支撑网络、可依据内存亦可耐久化的日志型、Key-Value 数据存储,并供给多种言语的 API。

简介

Redis 是内存数据库,数据都是存储在内存中,为了防止进程退出导致数据的永久丢掉,需求定时将 Redis 中的数据以某种方法(数据或指令)从内存保存到硬盘。当下次 Redis 重启时,运用耐久化文件完成数据康复。除此之外,为了进行灾难备份,能够将耐久化文件拷贝到一个长途位置。Redis 的耐久化机制有两种:

  • RDB(Redis Data Base) 内存快照
  • AOF(Append Only File) 增量日志

RDB 将当时数据保存到硬盘,AOF 则是将每次履行的写指令保存到硬盘(相似于 MySQL 的 Binlog)。AOF 耐久化的实时性更好,即当进程意外退出时丢掉的数据更少。

RDB耐久化

简介

RDB ( RedisData Base) 指的是在指定的时间距离内将内存中的数据集快照写入磁盘,RDB 是内存快照(内存数据的二进制序列化方法)的方法耐久化,每次都是从 Redis 中生成一个快照进行数据的全量备份。

长处:

  • 存储紧凑,节省内存空间。
  • 康复速度十分快。
  • 合适全量备份、全量仿制的场景,经常用于灾难康复(对数据的完好性和共同性要求相对较低的场合)。

缺陷:

  • 简单丢掉数据,简单丢掉两次快照之间 Redis 服务器中变化的数据。
  • RDB 经过 fork 子进程对内存快照进行全量备份,是一个重量级操作,频频履行本钱高。

Redis 持久化策略浅析

RDB 文件结构

在默许状况下,Redis 将数据库快照保存在姓名为 dump.rdb 的二进制文件中。RDB 文件结构由五个部分组成:

(1)长度为5字节的REDIS常量字符串。

(2)4字节的 db_version,标识 RDB 文件版别。

(3)databases:不定长度,包含零个或多个数据库,以及各数据库中的键值对数据。

(4)1字节的 EOF 常量,表明文件正文内容完毕。

(5)check_sum: 8字节长的无符号整数,保存校验和。

Redis 持久化策略浅析

数据结构举例,以下是数据库[0]和数据库[3]有数据的状况:

Redis 持久化策略浅析

RDB 文件的创立

手动指令触发

手动触发 RDB 耐久化的方法能够运用save指令和bgsave指令,这两个指令的差异如下:

save:履行save指令,堵塞 Redis 的其他操作,会导致 Redis 无法呼应客户端恳求,不主张运用。

bgsave:履行bgsave指令,Redis 后台创立子进程,异步进行快照的保存操作,此刻 Redis 依然能呼应客户端的恳求。

主动距离性保存

在默许状况下,Redis 将数据库快照保存在姓名为 dump.rdb的二进制文件中。能够对 Redis 进行设置,让它在“ N 秒内数据集至少有 M 个改动”这一条件被满意时,主动保存一次数据集。

比如说, 以下设置会让 Redis 在满意“ 60 秒内有至少有 10 个键被改动”这一条件时,主动保存一次数据集:save 60 10

Redis 的默许装备如下,三个设置满意其一即可触发主动保存:

save6010000
save30010
save9001

主动保存装备的数据结构

记载了服务器触发主动BGSAVE条件的saveparams特色。

lastsave特色:记载服务器最终一次履行SAVE或者BGSAVE的时间。

dirty特色:以及自最终一次保存 RDB 文件以来,服务器进行了多少次写入。

Redis 持久化策略浅析

备份进程

RDB 耐久化计划进行备份时,Redis 会单独 fork 一个子进程来进行耐久化,会将数据写入一个暂时文件中,耐久化完结后替换旧的 RDB 文件。在整个耐久化进程中,主进程(为客户端供给服务的进程)不参与 IO 操作,这样能确保 Redis 服务的高功能,RDB 耐久化机制合适对数据完好性要求不高但寻求高效康复的运用场景。下面展示 RDB 耐久化流程:

Redis 持久化策略浅析

关键履行进程如下

  1. Redis 父进程首要判别:当时是否在履行 save,或 bgsave/bgrewriteaof 的子进程,假如在履行则 bgsave 指令直接回来。bgsave/bgrewriteaof 的子进程不能一起履行,主要是依据功能方面的考虑:两个并发的子进程一起履行许多的磁盘写操作,可能引起严峻的功能问题。
  1. 父进程履行 fork 操作创立子进程,这个进程中父进程是堵塞的,Redis 不能履行来自客户端的任何指令。父进程 fork 后,bgsave 指令回来”Background saving started”信息并不再堵塞父进程,并能够呼应其他指令。
  1. 子进程进程对内存数据生成快照文件。
  1. 父进程在此期间接纳的新的写操作,运用 COW 机制写入。
  1. 子进程完结快照写入,替换旧 RDB 文件,随后子进程退出。

在生成 RDB 文件的进程中,在同步到磁盘和持续写入这个进程是如何处理数据不共同的状况呢?生成快照 RDB 文件时是否会对事务产生影响?

Fork 子进程的作用

上面说到了 RDB 耐久化进程中,主进程会 fork 一个子进程来担任 RDB 的备份,这里简单介绍一下 fork:

  • Linux 操作体系中的程序,fork 会产生一个和父进程完全相同的子进程。子进程与父进程一切的数据均共同,可是子进程是一个全新的进程,与原进程是父子进程联系。
  • 出于效率考虑,Linux 操作体系中运用 COW(Copy On Write)写时仿制机制,fork 子进程一般状况下与父进程共同运用一段物理内存,只要在进程空间中的内存产生修正时,内存空间才会仿制一份出来。

在 Redis 中,RDB 耐久化便是充分的运用了这项技术,Redis 在耐久化时调用 glibc 函数 fork 一个子进程,全权担任耐久化工作,这样父进程依然能持续给客户端供给服务。fork 的子进程初始时与父进程(Redis 的主进程)同享同一块内存;当耐久化进程中,客户端的恳求对内存中的数据进行修正,此刻就会经过 COW (Copy On Write) 机制对数据段页面进行分离,也便是仿制一块内存出来给主进程去修正。

Redis 持久化策略浅析

经过 fork 创立的子进程能够取得和父进程完全相同的内存空间,父进程对内存的修正关于子进程是不行见的,两者不会相互影响;

经过 fork 创立子进程时不会马上触发许多内存的拷贝,选用的是写时拷贝 COW (Copy On Write)。内核只为新生成的子进程创立虚拟空间结构,它们来仿制于父进程的虚拟究竟结构,可是不为这些段分配物理内存,它们同享父进程的物理空间,当父子进程中有更改相应段的行为产生时,再为子进程相应的段分配物理空间;

AOF 耐久化

简介

AOF (Append Only File) 是把一切对内存进行修正的指令(写操作)以独立日志文件的方法进行记载,重启时经过履行 AOF 文件中的 Redis 指令来康复数据。相似MySql bin-log 原理。AOF 能够处理数据耐久化实时性问题,是现在 Redis 耐久化机制中主流的耐久化计划。

Redis 持久化策略浅析

长处:

  • 数据的备份更加完好,丢掉数据的概率更低,合适对数据完好性要求高的场景
  • 日志文件可读,AOF 可操作性更强,可经过操作日志文件进行修复

缺陷:

  • AOF 日志记载在长期运转中逐渐庞大,康复起来十分耗时,需求定时对 AOF 日志进行减肥处理
  • 康复备份速度比较慢
  • 同步写操作频频会带来功能压力

AOF 文件内容

被写入 AOF 文件的一切指令都是以 RESP 格局保存的,是纯文本格局保存在 AOF 文件中。

Redis 客户端和服务端之间运用一种名为RESP(REdis Serialization Protocol)的二进制安全文本协议进行通讯。

下面以一个简单的 SET 指令进行举例:

redis>SETmykey"hello"//客户端指令
OK

客户端封装为以下格局(每行用\r\n分隔)

*3
$3
SET
$5
mykey
$5
hello

AOF 文件中记载的文本内容如下

*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n//多出一个SELECT0指令,用于指定数据库,为体系主动添加
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$5\r\nhello\r\n

AOF 耐久化完成

AOF 耐久化计划进行备份时,客户端一切恳求的写指令都会被追加到 AOF 缓冲区中,缓冲区中的数据会依据 Redis 装备文件中装备的同步战略来同步到磁盘上的 AOF 文件中,追加保存每次写的操作到文件结尾。一起当 AOF 的文件到达重写战略装备的阈值时,Redis 会对 AOF 日志文件进行重写,给 AOF 日志文件减肥。Redis 服务重启的时分,经过加载 AOF 日志文件来康复数据。

Redis 持久化策略浅析

AOF 的履行流程包含:

指令追加(append)

Redis 先将写指令追加到缓冲区 aof_buf,而不是直接写入文件,主要是为了防止每次有写指令都直接写入硬盘,导致硬盘 IO 成为 Redis 负载的瓶颈。

structredisServer{
//其他域...
sdsaof_buf;//sds相似于Java中的String
//其他域...
}

文件写入(write)和文件同步(sync)

依据不同的同步战略将 aof_buf 中的内容同步到硬盘;

Linux 操作体系中为了提升功能,运用了页缓存(page cache)。当咱们将 aof_buf 的内容写到磁盘上时,此刻数据并没有真实的落盘,而是在 page cache 中,为了将 page cache 中的数据真实落盘,需求履行 fsync / fdatasync 指令来强制刷盘。这边的文件同步做的便是刷盘操作,或者叫文件刷盘可能更简单理解一些。

AOF 缓存区的同步文件战略由参数 appendfsync 控制,有三种同步战略,各个值的含义如下:

  • always:指令写入 aof_buf 后当即调用体系 write 操作和体系 fsync 操作同步到 AOF 文件,fsync 完结后线程回来。这种状况下,每次有写指令都要同步到 AOF 文件,硬盘 IO 成为功能瓶颈,Redis 只能支撑大约几百TPS写入,严峻下降了 Redis 的功能;即便是运用固态硬盘(SSD),每秒大约也只能处理几万个指令,而且会大大下降 SSD 的寿数。可靠性较高,数据基本不丢掉。
  • no:指令写入 aof_buf 后调用体系 write 操作,不对 AOF 文件做 fsync 同步;同步由操作体系担任,一般同步周期为30秒。这种状况下,文件同步的时间不行控,且缓冲区中堆积的数据会许多,数据安全性无法确保。
  • everysec:指令写入 aof_buf 后调用体系 write 操作,write 完结后线程回来;fsync 同步文件操作由专门的线程每秒调用一次。everysec 是前述两种战略的折中,是功能和数据安全性的平衡,因而是 Redis 的默许装备,也是咱们引荐的装备。

文件重写(rewrite)

定时重写 AOF 文件,到达压缩的目的。

AOF 重写是 AOF 耐久化的一个机制,用来压缩 AOF 文件,经过 fork 一个子进程,重新写一个新的 AOF 文件,该次重写不是读取旧的 AOF 文件进行仿制,而是读取内存中的Redis数据库,重写一份 AOF 文件,有点相似于 RDB 的快照方法。

文件重写之所以能够压缩 AOF 文件,原因在于:

  • 过期的数据不再写入文件
  • 无效的指令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等
  • 多条指令能够合并为一个:如 sadd myset v1, sadd myset v2, sadd myset v3 能够合并为 sadd myset v1 v2 v3。不过为了防止单条指令过大造成客户端缓冲区溢出,关于 list、set、hash、zset类型的 key,并不一定只运用一条指令;而是以某个常量为界将指令拆分为多条。这个常量在 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD 中界说,不行更改,2.9版别中值是64。

AOF重写

前面提到 AOF 的缺陷时,说过 AOF 归于日志追加的方法来存储 Redis 的写指令,这会导致许多冗余的指令存储,然后使得 AOF 日志文件十分庞大,比如同一个 key 被写了 10000 次,最终却被删除了,这种状况不仅占内存,也会导致康复的时分十分缓慢,因而 Redis 供给重写机制来处理这个问题。Redis 的 AOF 耐久化机制履行重写后,保存的仅仅康复数据的最小指令集,咱们假如想手动触发能够运用如下指令:

bgrewriteaof

文件重写时机

相关参数:

  • aof_current_size:表明当时 AOF 文件空间
  • aof_base_size:表明上一次重写后 AOF 文件空间
  • auto-aof-rewrite-min-size: 表明运转 AOF 重写时文件的最小体积,默许为64MB
  • auto-aof-rewrite-percentage: 表明当时 AOF 重写时文件空间(aof_current_size)超越上一次重写后 AOF 文件空间(aof_base_size)的比值多少后会重写。

一起满意下面两个条件,则触发 AOF 重写机制:

  • aof_current_size 大于 auto-aof-rewrite-min-size
  • 当时 AOF 相比上一次 AOF 的增长率:(aof_current_size – aof_base_size)/aof_base_size 大于或等于 auto-aof-rewrite-percentage

AOF重写流程如下:

  1. bgrewriteaof 触发重写,判别是否存在 bgsave 或者 bgrewriteaof 正在履行,存在则等候其履行完毕再履行
  1. 主进程 fork 子进程,防止主进程堵塞无法供给服务,相似 RDB
  1. 子进程遍历 Redis 内存快照中数据写入暂时 AOF 文件,一起会将新的写指令写入 aof_buf 和 aof_rewrite_buf 两个重写缓冲区,前者是为了写回旧的 AOF 文件,后者是为了后续刷新到暂时 AOF 文件中,防止快照内存遍历时新的写入操作丢掉
  2. 子进程完毕暂时 AOF 文件写入后,通知主进程
  3. 主进程会将上面 3 中的 aof_rewirte_buf 缓冲区中的数据写入到子进程生成的暂时 AOF 文件中
  4. 主进程运用暂时 AOF 文件替换旧 AOF 文件,完结整个重写进程。

Redis 持久化策略浅析

在实际中,为了防止在履行指令时造成客户端输入缓冲区溢出,重写程序会查看调集元素数量是否超越 REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值,假如超越了,则会运用多个指令来记载,而不单单运用一条指令。

Redis 2.9版别中该常量为64,假如一个指令的调集键包含超越了64个元素,重写程序会拆成多个指令。

Redis 持久化策略浅析

AOF重写是一个有歧义的姓名,该功能是经过直接读取数据库的键值对完成的,程序无需对现有AOF文件进行任何读入、剖析或者写入操作。

思考

AOF 与 WAL

Redis 为什么考虑运用 AOF 而不是 WAL 呢?

许多数据库都是选用的 Write Ahead Log(WAL)写前日志,其特色便是先把修正的数据记载到日志中,再进行写数据的提交,能够便利经过日志进行数据康复。

可是 Redis 选用的却是 AOF(Append Only File)写后日志,特色便是先履行写指令,把数据写入内存中,再记载日志。

假如先让体系履行指令,只要指令能履行成功,才会被记载到日志中。因而,Redis 运用写后日志这种方法,能够防止出现记载错误指令的状况。

另外还有一个原因便是:AOF 是在指令履行后才记载日志,所以不会堵塞当时的写操作。

AOF 和 RDB 之间的相互作用

在版别号大于等于2.4的 Redis 中,BGSAVE 履行的进程中,不行以履行 BGREWRITEAOF。反过来说,在 BGREWRITEAOF 履行的进程中,也不行以履行 BGSAVE。这能够防止两个 Redis 后台进程一起对磁盘进行许多的 I/O 操作。

假如 BGSAVE 正在履行,而且用户显现地调用 BGREWRITEAOF 指令,那么服务器将向用户回复一个 OK 状况,并告知用户,BGREWRITEAOF 现已被预订履行:一旦 BGSAVE 履行完毕 BGREWRITEAOF 就会正式开端。

当 Redis 启动时,假如 RDB 耐久化和 AOF 耐久化都被打开了,那么程序会优先运用 AOF 文件来康复数据集,由于 AOF 文件所保存的数据一般是最完好的。

混合耐久化

Redis4.0 后大部分的运用场景都不会单独运用 RDB 或者 AOF 来做耐久化机制,而是兼顾二者的优势混合运用。其原因是 RDB 虽然快,可是会丢掉比较多的数据,不能确保数据完好性;AOF 虽然能尽可能确保数据完好性,可是功能确实是一个诟病,比如重放康复数据。

Redis从4.0版别开端引入 RDB-AOF 混合耐久化形式,这种形式是依据 AOF 耐久化形式构建而来的,混合耐久化经过aof-use-rdb-preamble yes敞开。

那么 Redis 服务器在履行 AOF 重写操作时,就会像履行 BGSAVE 指令那样,依据数据库当时的状况生成出相应的 RDB 数据,并将这些数据写入新建的 AOF 文件中,至于那些在 AOF 重写开端之后履行的 Redis 指令,则会持续以协议文本的方法追加到新 AOF 文件的结尾,即已有的 RDB 数据的后边。

换句话说,在敞开了 RDB-AOF 混合耐久化功能之后,服务器生成的 AOF 文件将由两个部分组成,其中位于 AOF 文件最初的是 RDB 格局的数据,而跟在 RDB 数据后边的则是 AOF 格局的数据。

当一个支撑 RDB-AOF 混合耐久化形式的 Redis 服务器启动并载入 AOF 文件时,它会查看 AOF 文件的最初是否包含了 RDB 格局的内容。

  • 假如包含,那么服务器就会先载入最初的 RDB 数据,然后再载入之后的 AOF 数据。
  • 假如 AOF 文件只包含 AOF 数据,那么服务器将直接载入 AOF 数据。

其日志文件结构如下:

Redis 持久化策略浅析

总结

最终来总结这两者,究竟用哪个更好呢?

  • 引荐是两者均敞开。
  • 假如对数据不灵敏,能够选单独用 RDB。
  • 假如仅仅做纯内存缓存,能够都不用。

▶ 参考文献

  1. 黄健宏.Redis规划与完成:机械工业出版社,2014
  1. redis.cn/topics/pers…

▶ 加入咱们

咱们来自字节跳动飞书商业使用研发部(Lark Business Applications),目前咱们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了工作区域。咱们关注的产品范畴主要在企业经历管理软件上,包含飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 范畴体系,也包含飞书审批、OA、法务、财务、收购、差旅与报销等体系。欢迎各位加入咱们。

扫码发现职位&投递简历

Redis 持久化策略浅析