我的新课《C2C 电商系统微服务架构120天实战训练营》在公众号儒猿技术窝上线了,感兴趣的同学,可以长按扫描下方二维码了解课程详情:
开篇
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的过程:
Redis Client端通过向Redis Server 发起save命令请求RDB持久化操作。
Redis Server接受到命令以后,将当前数据库快照保存到RDB文件中。
由于save命令是同步操作,因此如果此时有其他Redis Client也向Redis Server发起save操作,会被阻塞直到第一个Redis Client完成save命令为止。
图2 save同步方式触发RDB持久化
bgsave同步方式触发RDB持久化
如图3所示,异步方式过程如下:
依旧是Redis Client发起命令,不过命令改成了bgsave(background save有后台运行的意思),照旧请求Redis Server。
Redis Server接受到请求以后会fork出一个Redis子进程。
这个子进程用来创建RDB文件。由于这个过程是异步的,因此Redis Server在启动子进程以后还可以接受其他请求。
Redis 子进程创建RDB文件以后会把成功的消息返回给Redis Server。
由于bgsave命令是异步操作,如果此时有其他Redis Client同时请求Redis Server并不会被阻塞。Redis Server会响应请求,同样也会fork出对应的子进程进行RDB文件的创建。
图3 bgsave异步方式触发RDB持久化
自动配置化的方式触发
如图4所示,这种方式可以理解为读取配置文件的方式。看下面的三个步骤:
Redis Server直接读取Redis 配置文件的内容,获取RDB持久化的信息。
在Redis配置文件中配置了对应的save命令用来代替Redis Client请求的命令。其配置内容包括save命令、秒和修改次数。按照图中的例子来说,“save 500 3” 的意思是,在500秒的时间内如果Redis 数据库有3次修改就进行save请求,也就是请求RDB的持久化操作。
一旦满足配置文件中的条件,Redis Server就会执行对应的save操作进行持久化。
图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 给大家介绍具体流程:
Redis Client作为命令的来源,会有多个源头以及源源不断的请求命令。
在这些命令到达Redis Server 以后,并不是直接写入AOF文件,会将其这些命令先放入AOF缓存中进行保存。这里的AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。
AOF缓冲会根据对应的策略将命令写入磁盘上的AOF文件。
AOF文件随着写入文件内容的增加,会根据规则进行命令的合并,这里叫做AOF重写,从而起到AOF文件压缩的目的。
当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中进行如下设置。
#开启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策略。
把本文学习笔记的思维导图分享如下。