11

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

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng%3D%3D&%3Bmid=2247491812&%3Bidx=1&%3Bsn=8103aab54ae990cb0eaad0226aac3efe
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

m2u67z2.jpg!mobile

我的新课 《C2C 电商系统微服务架构120天实战训练营》 在公众号 儒猿技术窝 上线了,感兴趣的同学,可以长按扫描下方二维码了解课程详情:

课程大纲请参见文末

YBrIfiJ.png!mobile

开篇

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文件的形式保存到磁盘中。

mQj2EbA.png!mobile

图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命令为止。

nq2aMrI.png!mobile

图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文件的创建。

Vb6vIb3.png!mobile

图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操作进行持久化。

QvqUF3b.png!mobile

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

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

# 900秒内有1次修改、300秒内有10次修改、60秒内有10000次修改

#满足任何以上条件,触发RDB持久化。

save 900 1

save 300 10

save 60 10000

# 当快照操作bgsave出错时,是否停止持久化?yes 表示“是”,no表示“否”。

stop-writes-on-bgsave-error yes

# 是否压缩?yes表示“是”,no表示“否”,默认选择yes。

rdbcompression yes

# 对rdb数据进行校验, 表示写入文件和读取文件时是否开启 RDB 文件检查。

# yes表示“是”,no表示“否”,默认选择yes。

# 选择yes表示在Redis加载RDB需要检查文件是否损坏,如果存在损坏会停止启动。

rdbchecksum yes

# 设置rdb的文件名

dbfilename dump.rdb

# RDB文件的路径,如果不单独指定,默认是redis的启动路径。

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文件载入数据。

zmuUba.png!mobile

图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重写。

vaEvEnn.png!mobile

图6 AOF重写流程图

AOF配置重写

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

fQB7bur.png!mobile

通过上面的配置可以得到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中进行如下设置。

#开启AOF模式 ,yes表示“开启AOF模式”。

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策略。

把本文学习笔记的思维导图分享如下。

3m6j6v2.png!mobile

END

征稿:愿意技术分享的朋友,欢迎投稿,每篇文章提供 800 ~ 1000 元的稿酬

投稿请扫描下方二维码,添加微信:jeversoncui

AZZVzqe.png!mobile

iQVbE3a.jpg!mobile

V3U3Ezv.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK