|
|
|
|
公众号矩阵

一杯茶的功夫,上手Redis持久化机制

Redis作为最常用的内存数据库,通常来说数据存储在内存中,为了避免Redis服务器进程退出导致内存中的数据消失。

作者:崔皓 来源:石杉的架构笔记|2020-12-29 10:06

 

本文转载自微信公众号「石杉的架构笔记」,作者崔皓 。转载本文请联系石杉的架构笔记公众号。

开篇

Redis作为最常用的内存数据库,通常来说数据存储在内存中,为了避免Redis服务器进程退出导致内存中的数据消失。Redis提出了持久化机制,也就是把内存中的数据保存到磁盘中,从而提高数据存储的可靠性。为此主流数据库会提供两类持久化方案,它们是“快照”存储和“日志”存储。相应地Redis提供了RDB持久化和AOF持久化与之对应。其中RDB是以快照的方式存储内存数据到磁盘上,而AOF是以日志追加的方式进行存储。下面就围绕这两种持久化方式展开如下内容:

  • RDB 文件结构
  • RDB触发机制以及流程
  • AOF持久化流程
  • AOF缓冲区同步文件策略
  • AOF重写
  • RDB和AOF的比较

RDB持久化

RDB是Redis Database 的缩写,其作用是在某一个时间点,将Redis存储在内存中的数据生成快照并存储到磁盘等介质上,存在这个磁盘介质上的文件就是RDB文件。“快照”顾名思义就是好像照相一样保存当时的数据,这里的RDB文件是一个二进制的文件,并且是经过压缩的。因为RDB文件是保存在硬盘中的,即使Redis服务器进程退出,甚至运行Redis服务器的计算机宕机,但只要RDB文件仍然存在,Redis服务器就可以用它来还原数据库状态。如图1 所示,可以想象Redis数据库在时间轴上有位于不同时间点的时候都有一个数据库状态,可以把它们想象成一个个切片。图上标注出两个时间点的两个数据库切片,RDB持久化做的事情就是顺着绿色箭头的方向将数据库状态的“切片”以RDB文件的形式保存到磁盘中。

图1 将数据库状态保存为RDB文件

RDB 文件结构

虽然说RDB是一个压缩过的二进制文件,但是它的文件结构也需要有基本的了解,这样有助于我们理解其发挥的作用。如表格1所示,RDB文件由5个部分组成,按照从左到右的顺序依次是:

  • 文件最开头是“REDIS”部分,其长度为5个字节,保存着“REDIS”五个字符。通过这五个字符,程序可以在载入文件时可以判断所载入的文件是否是RDB文件。
  • 接下来是“db_version”长度为4字节,是一个字符串表示的整数,它记录了RDB文件的版本号,例如:"0006"就代表RDB文件的版本为第六版。
  • “databases”中可以包含着零个或任意多个数据库。
  • “EOF”常量的长度为1字节,是 RDB文件正文结束的标识,当载入程序读取到个值的时,就意味着数据库的所有键值对都已经加载完毕了。
  • “check_sum”是一个8字节长的无符号整数,保存着一个校验和。这个校验和是通过对“REDIS”、“db_version”、“databases”、以及“EOF”四个部分的内容进行计算得出的。Redis服务器在载入RDB文件时,会将载入数据所计算出的校验和与check_sum所记录的校验和进行对比,以此来判断RDB文件是否损坏。

REDIS

db_version

databases

EOF

check_sum

表格1 RDB的文件结构

如表格2所示,其表示一个databases部分为空的RDB文件:文件以"REDIS"开头,表示这是一个RDB文件,之后的"0006"表示数据库版本是第六版。因为databases为空所以这里没有数据库的信息,所以版本号之后直接跟着“EOF”常量,最后的6265312314761934562是文件的校验和。

REDIS

“0006”

EOF

6265312314761934562

表格2 RDB文件例子

RDB 触发机制以及流程

在认识了RDB文件的结构以后,再来看看Redis 如何触发RDB持久化操作,以及其具体流程是如何的。这里包括三部分的内容,分别是save同步方式触发、bgsave 异步方式触发以及自动配置化的方式触发。

save同步方式触发RDB持久化

如图2所示,描述了save 同步方式持久化RDB的过程:

  1. Redis Client端通过向Redis Server 发起save命令请求RDB持久化操作。
  2. Redis Server接受到命令以后,将当前数据库快照保存到RDB文件中。
  3. 由于save命令是同步操作,因此如果此时有其他Redis Client也向Redis Server发起save操作,会被阻塞直到第一个Redis Client完成save命令为止。

图2 save同步方式触发RDB持久化

bgsave同步方式触发RDB持久化

如图3所示,异步方式过程如下:

  1. 依旧是Redis Client发起命令,不过命令改成了bgsave(background save有后台运行的意思),照旧请求Redis Server。
  2. Redis Server接受到请求以后会fork出一个Redis子进程。
  3. 这个子进程用来创建RDB文件。由于这个过程是异步的,因此Redis Server在启动子进程以后还可以接受其他请求。
  4. Redis 子进程创建RDB文件以后会把成功的消息返回给Redis Server。
  5. 由于bgsave命令是异步操作,如果此时有其他Redis Client同时请求Redis Server并不会被阻塞。Redis Server会响应请求,同样也会fork出对应的子进程进行RDB文件的创建。

图3 bgsave异步方式触发RDB持久化

自动配置化的方式触发

如图4所示,这种方式可以理解为读取配置文件的方式。看下面的三个步骤:

  1. Redis Server直接读取Redis 配置文件的内容,获取RDB持久化的信息。
  2. 在Redis配置文件中配置了对应的save命令用来代替Redis Client请求的命令。其配置内容包括save命令、秒和修改次数。按照图中的例子来说,“save 500 3” 的意思是,在500秒的时间内如果Redis 数据库有3次修改就进行save请求,也就是请求RDB的持久化操作。
  3. 一旦满足配置文件中的条件,Redis Server就会执行对应的save操作进行持久化。

图4 读取配置文件进行RDB持久化

一般而言Redis的配置信息会放到redis.conf配置文件中进行存储,其包含很多内容,这里我们就RDB持久化的部分给大家做简单介绍。redis.conf文件中找到“SNAPSHOTTING”(快照)的部分。看如下几个配置项:

  1. # 900秒内有1次修改、300秒内有10次修改、60秒内有10000次修改 
  2. #满足任何以上条件,触发RDB持久化。 
  3. save 900 1 
  4. save 300 10 
  5. save 60 10000 
  6.  
  7. # 当快照操作bgsave出错时,是否停止持久化?yes 表示“是”,no表示“否”。 
  8. stop-writes-on-bgsave-error yes 
  9.  
  10. # 是否压缩?yes表示“是”,no表示“否”,默认选择yes。 
  11. rdbcompression yes 
  12.  
  13. # 对rdb数据进行校验, 表示写入文件和读取文件时是否开启 RDB 文件检查。 
  14. # yes表示“是”,no表示“否”,默认选择yes。 
  15. # 选择yes表示在Redis加载RDB需要检查文件是否损坏,如果存在损坏会停止启动。 
  16. rdbchecksum yes 
  17.  
  18. # 设置rdb的文件名 
  19. dbfilename dump.rdb 
  20.  
  21. # RDB文件的路径,如果不单独指定,默认是redis的启动路径。 
  22. dir ./ 

关于RDB持久化恢复Redis数据方面也比较简单,将RDB持久化文件 (例如:dump.rdb) 移动到 Redis 安装目录并启动Redis服务就可以了。可以通过 Redis 中的“CONFIG GET dir”命令获取Redis的安装目录。

由于RDB是一个压缩的二进制文件,其代表Redis在某一个时间点上的快照。其适合数据库备份和全量复制的场景。比如定期给数据库进行备份,把RDB文件拷贝到其他的服务器上,以及用于灾备。同样是因为压缩的原因,RDB的加载速度比AOF也要快。

AOF持久化

上面介绍了RDB的执行方式和流程,这种方式没有办法做到实时持久化的要求。因为无论是save还是bgsave每次运行都要消耗大量的资源(CPU、内存、磁盘)。随着数据库本身容量的增加每次备份的数据量也随之增加。同时RDB是二进制保存,当Redis版本演进过程中有多个格式的RDB版本,会存在老版本RDB与新版本格式兼容的问题。正式因为RDB的这些问题,Redis提出了AOF的持久化方式。AOF(append only file),是以日志的方式记录每次写入的命令,在Redis Server启动的时候会重新执行AOF文件中的命令,从而达到恢复数据的目的。AOF可以解决数据持久化的实时性问题,也是当前Redis主流的持久化方式。

AOF持久化流程

上面提到了AOF持久化的过程就是日志不断追加的过程,这里通过图5 给大家介绍具体流程:

  1. Redis Client作为命令的来源,会有多个源头以及源源不断的请求命令。
  2. 在这些命令到达Redis Server 以后,并不是直接写入AOF文件,会将其这些命令先放入AOF缓存中进行保存。这里的AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。
  3. AOF缓冲会根据对应的策略将命令写入磁盘上的AOF文件。
  4. AOF文件随着写入文件内容的增加,会根据规则进行命令的合并,这里叫做AOF重写,从而起到AOF文件压缩的目的。
  5. 当Redis Server 服务器重启的时候会从AOF文件载入数据。

图5 AOF 处理流程图

AOF缓冲区同步文件策略

上面提到了Redis 会将命令先写入到AOF缓冲区,再写入AOF文件。这里介绍一下AOF缓冲区同步文件的三个策略。

  • always 策略:命令写入AOF缓冲区以后会调用系统fsync 操作同步到AOF文件,fsync完成后线程返回。这里的fsync是针对单个文件的操作,在进行磁盘同步的时候会阻塞直到写入磁盘完成以后返回,从而保证数据持久化的完成。
  • everysec 策略:命令写入AOF缓冲区以后调用write操作,write完成后线程返回。此操作会有专门线程执行每秒执行一次。这里的write操作会触发延迟写(delayed write)机制,Linux 内核提供页缓冲区来提高硬盘IO性能。也就是说write 操作写入系统缓冲区以后就返回了,同步硬盘依赖于操作系统调度机制完成。(Redis默认配置)
  • no策略:此种刷新策略是根据操作系统来决定的,也就是由操作系统来决定什么时候将缓冲区的数据写入到磁盘中。由于是操作系统来决定持久化,所以这种方式是不可控的。

AOF 重写

AOF缓冲区会将Redis Client请求的命令源源不断地同步到AOF文件中,同时AOF文件会不断增大,这里就需要AOF重写。AOF重写就是把Redis进程内的数据转化为写命令同步到新的AOF文件的过程。其目的就是使重写后的AOF文件变得更小:

  • 进程内已经超时的数据不会再写入AOF文件中。
  • 旧AOF文件含有的无效命令,可以通过进程内的数据直接生成,新的AOF文件只保留最终的数据写入命令。例如就文件中存在三条命令,它们依次是“set hello A”、 “set hello B”和“set hello C”,对同一个key 进行负值只有最后一句“set hello C”是起效的,所以这三条命令会被“set hello C”一条命令替换,并且保存到新的AOF文件中。
  • 另外,多条写命令可以合并成一个。例如依次存在三个命令:“lpush list A”、 “lpush list B”和“lpush list C”,这里就可以合并为一条命令“lpush list A B C”。

AOF重写不仅降低了文件的占用空间,同时更小的AOF也可以更快地被Redis加载。

说完了AOF重写的定义以后,下面来看看AOF重写的流程。一般而言有两种方式可以执行重写操作,分别是:bgrewriteaof 命令和AOF重写配置。

bgrewriteaof命令重写

如图6 所示,整个执行过程由三步组成:

  • Redis Client发起bgrewriteaof命令,这个命令是一个异步命令。由于Redis Server 在接受bgrewriteaof命令的同时,还可以接受其他Redis Client的命令,因此后面的第二步和第三步实际上是并行进行的。第二步:进行AOF重写,在同时第三步:还会接受其他非重写的命令请求。
  • Redis Server接受到这个命令以后,会启动一个Redis的子进程用来执行AOF重写操作。这个重写过程实际上是针对Redis内存中的数据进行回溯,也就是下方红色区域的“AOF重写缓冲区”。
  • 正如在第一步中提到的,在进行第二步的同时第三步还在接受客户端的请求并且通过“AOF缓冲区”保存到“旧AOF文件”中。
  • 最终,完成AOF重写操作以后将“新AOF文件”写入到“旧AOF文件”中完成AOF重写。

图6 AOF重写流程图

AOF配置重写

实际上是通过AOF的配置文件中的配置值来确定重写的时机。配置如下:

通过上面的配置可以得到AOF重写的机制如下:

  • 当AOF文件当前尺寸大于AOF重写的最小尺寸的时候就触发重写机制。通过上面配置来形成表达式就是:aof-current-size> auto-aof-rewrite-min-size
  • 当AOF文件当前尺寸减去AOF文件本身尺寸的值除以AOF文件本身的尺寸得到的结果大于AOF文件重写比率的时候就需要出发重写机制。

表达式就是:aof-current-size- aof-base-size/ aof-base-size > auto-aof-rewrite-percentage

由于Redis的配置文件中RDB是默认配置。AOF需要手动开启。

需要到Redis的配置文件redis.conf中进行如下设置。

  1. #开启AOF模式 ,yes表示“开启AOF模式”。 
  2.  
  3. appendonly yes 

与RDB持久化文件恢复数据一样,只需要将AOF文件 移动到 Redis 安装目录,并启动Redis服务就可以在Redis启动的时候加载AOF文件恢复数据了。

通过上面对AOF的描述可以看出,AOF具有数据完整,安全性高(秒级数据丢失)等优点。同时AOF文件以追加日志文件的形式存在,且写入操作是以Redis协议格式保存的,因此其内容是可读性强,适合误删时的紧急恢复。不过,相对于RDB而言其文件尺寸较大,会占用更多Redis启动加载数据的时间。

RDB和AOF的比较

前面介绍了RDB和AOF两种机制,这里将它们做一个简单对比。

  • 启动优先级:假设Redis 同时打开了RDB和AOF持久化功能,当Redis重启的时候会优先加载AOF,因为AOF数据更新的频率更高,会保存更新的数据。
  • 体积大小/恢复速度:RDB是使用二进制压缩的模式保存,因此体积会比较小,在Redis恢复的时候加载的速度也会更快。相反AOF写入的是日志的形式,因此体积会较大,恢复速度也会慢些。
  • 数据安全性:RDB是以快照的模式保存数据,对数据的保存不是实时性的,会有丢失数据的可能性。而在这方面AOF的日志方式数据丢失的几率会比RDB好很多(例如:everysec)。
  • 资源消耗:RDB显示需要消耗的资源会更大,因为每次将全量的数据保存到磁盘中。而AOF每次可以保存增量的Redis数据。

总结

本文从为什么需要数据库持久化作为切入点,谈到Redis中的两类数据库持久化机制:RDB和AOF。其中针对RDB持久化通过介绍RDB文件结构、触发持久化的机制和流程进行了阐述。其包括:save同步方式、bgsave同步方式和自动配置方式。针对AOF持久化,通过AOF持久化流程、缓冲区同步文件策略以及AOF重写机制进行了介绍。其中缓冲区同步策略包括:always策略、everysec策略和no策略。

【编辑推荐】

  1. 经常用Redis,这些坑你知道吗?
  2. 分布式锁用Redis好?还是Zookeeper好?
  3. Redis快速入门
  4. Redis数据库实战培训课程_Redis集群配置_Redis培训_NoSQL数据库02
  5. 了解 Redis 列表基本原理,一文足矣!
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢
24H热文
一周话题
本月获赞

订阅专栏+更多

数据湖与数据仓库的分析实践攻略

数据湖与数据仓库的分析实践攻略

助力现代化数据管理:数据湖与数据仓库的分析实践攻略
共3章 | 创世达人

1人订阅学习

云原生架构实践

云原生架构实践

新技术引领移动互联网进入急速赛道
共3章 | KaliArch

30人订阅学习

数据中心和VPDN网络建设案例

数据中心和VPDN网络建设案例

漫画+案例
共20章 | 捷哥CCIE

202人订阅学习

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微