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 子进程对内存快照进行全量备份,是一个重量级操作,频频履行本钱高。
RDB 文件结构
在默许状况下,Redis 将数据库快照保存在姓名为 dump.rdb 的二进制文件中。RDB 文件结构由五个部分组成:
(1)长度为5字节的REDIS
常量字符串。
(2)4字节的 db_version,标识 RDB 文件版别。
(3)databases:不定长度,包含零个或多个数据库,以及各数据库中的键值对数据。
(4)1字节的 EOF 常量,表明文件正文内容完毕。
(5)check_sum: 8字节长的无符号整数,保存校验和。
数据结构举例,以下是数据库[0]和数据库[3]有数据的状况:
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 文件以来,服务器进行了多少次写入。
备份进程
RDB 耐久化计划进行备份时,Redis 会单独 fork 一个子进程来进行耐久化,会将数据写入一个暂时文件中,耐久化完结后替换旧的 RDB 文件。在整个耐久化进程中,主进程(为客户端供给服务的进程)不参与 IO 操作,这样能确保 Redis 服务的高功能,RDB 耐久化机制合适对数据完好性要求不高但寻求高效康复的运用场景。下面展示 RDB 耐久化流程:
关键履行进程如下
- Redis 父进程首要判别:当时是否在履行 save,或 bgsave/bgrewriteaof 的子进程,假如在履行则 bgsave 指令直接回来。bgsave/bgrewriteaof 的子进程不能一起履行,主要是依据功能方面的考虑:两个并发的子进程一起履行许多的磁盘写操作,可能引起严峻的功能问题。
- 父进程履行 fork 操作创立子进程,这个进程中父进程是堵塞的,Redis 不能履行来自客户端的任何指令。父进程 fork 后,bgsave 指令回来”Background saving started”信息并不再堵塞父进程,并能够呼应其他指令。
- 子进程进程对内存数据生成快照文件。
- 父进程在此期间接纳的新的写操作,运用 COW 机制写入。
- 子进程完结快照写入,替换旧 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) 机制对数据段页面进行分离,也便是仿制一块内存出来给主进程去修正。
经过 fork 创立的子进程能够取得和父进程完全相同的内存空间,父进程对内存的修正关于子进程是不行见的,两者不会相互影响;
经过 fork 创立子进程时不会马上触发许多内存的拷贝,选用的是写时拷贝 COW (Copy On Write)。内核只为新生成的子进程创立虚拟空间结构,它们来仿制于父进程的虚拟究竟结构,可是不为这些段分配物理内存,它们同享父进程的物理空间,当父子进程中有更改相应段的行为产生时,再为子进程相应的段分配物理空间;
▶AOF 耐久化
简介
AOF (Append Only File) 是把一切对内存进行修正的指令(写操作)以独立日志文件的方法进行记载,重启时经过履行 AOF 文件中的 Redis 指令来康复数据。相似MySql bin-log 原理。AOF 能够处理数据耐久化实时性问题,是现在 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 日志文件来康复数据。
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重写流程如下:
- bgrewriteaof 触发重写,判别是否存在 bgsave 或者 bgrewriteaof 正在履行,存在则等候其履行完毕再履行
- 主进程 fork 子进程,防止主进程堵塞无法供给服务,相似 RDB
- 子进程遍历 Redis 内存快照中数据写入暂时 AOF 文件,一起会将新的写指令写入 aof_buf 和 aof_rewrite_buf 两个重写缓冲区,前者是为了写回旧的 AOF 文件,后者是为了后续刷新到暂时 AOF 文件中,防止快照内存遍历时新的写入操作丢掉
- 子进程完毕暂时 AOF 文件写入后,通知主进程
- 主进程会将上面 3 中的 aof_rewirte_buf 缓冲区中的数据写入到子进程生成的暂时 AOF 文件中
- 主进程运用暂时 AOF 文件替换旧 AOF 文件,完结整个重写进程。
在实际中,为了防止在履行指令时造成客户端输入缓冲区溢出,重写程序会查看调集元素数量是否超越 REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值,假如超越了,则会运用多个指令来记载,而不单单运用一条指令。
Redis 2.9版别中该常量为64,假如一个指令的调集键包含超越了64个元素,重写程序会拆成多个指令。
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 数据。
其日志文件结构如下:
▶总结
最终来总结这两者,究竟用哪个更好呢?
- 引荐是两者均敞开。
- 假如对数据不灵敏,能够选单独用 RDB。
- 假如仅仅做纯内存缓存,能够都不用。
▶ 参考文献
- 黄健宏.Redis规划与完成:机械工业出版社,2014
- redis.cn/topics/pers…
▶ 加入咱们
咱们来自字节跳动飞书商业使用研发部(Lark Business Applications),目前咱们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了工作区域。咱们关注的产品范畴主要在企业经历管理软件上,包含飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 范畴体系,也包含飞书审批、OA、法务、财务、收购、差旅与报销等体系。欢迎各位加入咱们。
扫码发现职位&投递简历