当前位置: 首页 > 科技观察

一杯茶,入门Redis持久化机制

时间:2023-03-16 21:56:54 科技观察

本文转载自微信公众号《石山的架构笔记》,作者崔浩。转载本文请联系石山架构笔记公众号。一开始,Redis是最常用的内存数据库。一般来说,数据存储在内存中。为了避免Redis服务器进程退出导致内存中的数据消失。Redis提出了一种持久化机制,将内存中的数据保存到磁盘中,从而提高数据存储的可靠性。为此,主流数据库提供了两种持久化方案,分别是“快照”存储和“日志”存储。相应的,Redis提供了RDB持久化和与之对应的AOF持久化。其中RDB以snapshot的形式将内存数据存储在磁盘上,而AOF则以logappend的形式存储。下面围绕这两种持久化方式展开内容:RDB文件结构RDB触发机制及过程AOF持久化过程AOF缓冲区同步文件策略AOF重写RDB与AOF对比RDB持久化作用是通过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文件的版本是第六版。“数据库”可以包含零个或任意数量的数据库。“EOF”常量的长度为1个字节,是RDB文件文本结束的标志。当加载程序读取到一个值时,就意味着数据库的所有键值对都加载完成了。“check_sum”是一个包含校验和的8字节无符号整数。此校验和是根据“REDIS”、“db_version”、“databases”和“EOF”的内容计算得出的。Redis服务器加载RDB文件时,会通过加载数据计算出的校验和与check_sum记录的校验和进行比较,判断RDB文件是否损坏。REDISdb_versiondatabasesEOFcheck_sum表1RDB文件结构如表2所示,表示一个databases部分为空的RDB文件:文件以“REDIS”开头,表示这是一个RDB文件,后面的“0006”表示a数据库版本为第六版。因为databases是空的,这里没有数据库信息,所以版本号后面直接跟“EOF”常量,最后的6265312314761934562就是文件的校验和。REDIS"0006"EOF6265312314761934562表2RDB文件示例RDB触发机制及流程了解了RDB文件的结构后,我们来看看Redis是如何触发RDB持久化操作的,具体流程是怎样的。这包括三个部分,分别是save同步触发、bgsave异步触发和自动配置触发。save同步方式触发RDB持久化,如图2所示,描述了同步方式中保存RDB持久化的过程:RedisClient通过向RedisServer发起save命令来请求RDB持久化操作。RedisServer收到命令后,将当前数据库快照保存到RDB文件中。由于save命令是一个同步操作,如果此时其他RedisClient也向RedisServer发起了save操作,则会被阻塞,直到第一个RedisClient完成save命令。图2save同步模式触发RDB持久化bgsave同步模式触发RDB持久化如图3所示,异步模式流程如下:RedisClient依然发起命令,只是命令改为bgsave(后台save表示运行在后台),照常请求RedisServer。RedisServer收到请求后会fork出一个Redis子进程。该子进程用于创建RDB文件。由于这个进程是异步的,RedisServer启动子进程后就可以接受其他请求了。Redis子进程创建RDB文件后,会向RedisServer返回成功信息。由于bgsave命令是异步操作,如果其他RedisClient同时请求RedisServer,不会阻塞。RedisServer会响应请求,同时也会fork出相应的子进程来创建RDB文件。图3bgsave异步触发RDB持久化自动配置如图4所示,这个方法可以理解为一种读取配置文件的方式。看下面三个步骤:RedisServer直接读取Redis配置文件的内容,获取RDB持久化信息。在Redis配置文件中配置了相应的save命令来代替RedisClient请求的命令。它的配置包括保存命令、秒数和修改时间。根据图中的例子,“save5003”的意思是如果Redis数据库在500秒内被修改了3次,就会发起一次save请求,即请求对RDB进行一次持久化操作。一旦满足配置文件中的条件,RedisServer就会执行相应的保存操作进行持久化。图4读取RDB持久化的配置文件一般来说,Redis的配置信息都会保存在redis.conf配置文件中,里面包含了很多内容。下面就RDB持久化部分给大家做一个简单的介绍。在redis.conf文件中找到“SNAPSHOTTING”(快照)部分。看以下配置项:#900秒内修改1次,300秒内修改10次,60秒内修改10000次#满足以上任意一个条件,触发RDB持久化。save9001save30010save6010000#快照操作bgsave失败时是否停止持久化?yes表示“是”,no表示“否”。stop-writes-on-bgsave-erroryees#是否压缩?yes表示“是”,no表示“否”,默认选择yes。rdbcompressionyes#检查rdb数据,表示写文件和读文件时是否开启rdb文件检查。#yes表示“是”,no表示“否”,默认选择yes。#选择yes表示在Redis中加载RDB需要检查文件是否损坏,如果有损坏则停止启动。rdbchecksumyes#设置rdb的文件名dbfilenamedump.rdb#rdb文件的路径,如果不单独指定,默认为redis的启动路径。dir./关于RDB持久化和Redis数据恢复也比较简单,只需要将RDB持久化文件(例如:dump.rdb)移动到Redis安装目录下,启动Redis服务即可。在Redis中可以通过“CONFIGGETdir”命令获取Redis的安装目录。由于RDB是一个压缩的二进制文件,它代表了Redis在某个时间点的快照。适用于数据库备份和全量复制场景。比如定期备份数据库,将RDB文件复制到其他服务器,用于容灾。同样因为压缩,RDB的加载速度比AOF快。AOF持久化上面介绍了RDB的执行方式和过程。这种方式不能满足实时持久化的要求。因为save和bgsave每次运行都会消耗大量的资源(CPU、内存、磁盘)。随着数据库本身容量的增加,每次备份的数据量也随之增加。同时,RDB是以二进制格式存储的。当Redis版本演进过程中存在多种格式的RDB版本时,会出现老版本RDB与新版本的兼容问题。正式因为RDB的这些问题,Redis提出了AOF的持久化方式。AOF(appendonlyfile)以日志的形式记录每一个写入的命令。当RedisServer启动时,会重新执行AOF文件中的命令,从而达到恢复数据的目的。AOF可以解决数据持久化的实时性问题,也是Redis主流的持久化方式。AOF持久化过程上面说到AOF持久化的过程就是不断追加日志的过程。这里通过图5来介绍具体的流程:RedisClient作为命令的来源,会有多个来源,源源不断的请求命令。这些命令到达RedisServer后,并不会直接写入AOF文件,而是先将这些命令放入AOF缓存中进行存储。这里的AOF缓冲区其实就是内存中的一块区域。存在的目的是在达到一定数量后将这些命令写入磁盘,避免频繁的磁盘IO操作。AOF缓冲会根据相应的策略将命令写入磁盘上的AOF文件。随着写入文件内容的增加,AOF文件会按照规则合并命令。这就叫做AOF重写,从而达到AOF文件压缩的??目的。RedisServer服务器重启时,会从AOF文件中加载数据。图5AOF处理流程图AOF缓冲区同步文件策略上面提到,Redis会先将命令写入AOF缓冲区,然后再写入AOF文件。下面介绍AOF缓冲区同步文件的三种策略。always策略:命令写入AOF缓冲区后,系统会调用fsync操作同步到AOF文件,fsync完成后线程返回。这里的fsync是针对单个文件的操作。在进行磁盘同步时,会阻塞直到写入磁盘后返回,以保证数据持久化的完成。everysec策略:命令写入AOF缓冲区后,调用写操作,写完成后线程返回。此操作将由专用线程每秒执行一次。这里的写操作会触发延迟写机制,Linux内核提供了pagebuffer来提高硬盘IO性能。也就是说写操作写入系统缓冲区后返回,硬盘的同步依赖于操作系统的调度机制。(Redis默认配置)无策略:这种刷新策略是根据操作系统决定的,即操作系统决定何时将缓冲区中的数据写入磁盘。由于操作系统决定了持久化,这种方式是不可控的。AOF重写AOF缓冲区,会不断将RedisClient请求的命令同步到AOF文件中,AOF文件会不断增长,所以这里需要进行AOF重写。AOF重写是将Redis进程中的数据转化为写入命令,并同步到新的AOF文件中的过程。它的目的是为了让重写后的AOF文件变小:过程中超时的数据不会写入AOF文件。旧AOF文件中包含的无效命令可以直接从进程中的数据生成,新AOF文件只保留最后的数据写入命令。例如文件中有3条命令,依次是“sethelloA”、“sethelloB”、“sethelloC”,只有最后一句“sethelloC”对同一个键有效,所以这三个命令将被一个命令“sethelloC”代替并保存在一个新的AOF文件中。此外,多个写命令可以合并为一个。例如,依次有“lpushlistA”、“lpushlistB”、“lpushlistC”三个命令,可以组合成一个命令“lpushlistABC”。AOF重写不仅减少了文件占用空间,而且更小的AOF可以更快地被Redis加载。说完AOF重写的定义,我们来看看AOF重写的过程。一般来说,进行重写操作的方式有两种,分别是:bgrewriteaof命令和AOF重写配置。bgrewriteaof命令的改写如图6所示,整个执行过程分为三个步骤:RedisClient发起bgrewriteaof命令,该命令为异步命令。由于RedisServer在接受bgrewriteaof命令的同时,还可以接受来自其他RedisClient的命令,所以第二步和第三步实际上是并行执行的。第二步:进行AOF改写,同时第三步:也会接受其他非改写的命令请求。RedisServer收到这个命令后,会启动一个Redis子进程进行AOF重写操作。这个重写过程实际上是对Redis内存中的数据进行回溯,也就是下图红色区域的“AOF重写缓冲区”。第一步提到,在执行第二步的同时,第三步还在接受客户端的请求,并通过“AOF缓冲区”保存到“旧AOF文件”中。最后,AOF改写操作完成后,将“新AOF文件”写入“旧AOF文件”,完成AOF改写。图6AOF重写流程图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由于RDB是Redis配置文件中的默认配置。AOF需要手动开启。需要在Redis配置文件redis.conf中进行如下设置。#启用AOF模式,yes表示“开启AOF模式”。appendonlyyes与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的log模式下数据丢失的概率会比RDB好很多(例如:everysec)。资源消耗:RDB显示需要消耗更多的资源,因为每次都会将全量的数据保存到磁盘中。而AOF可以每次保存增量的Redis数据。小结本文从为什么需要数据库持久化入手,讲了Redis中两种数据库持久化机制:RDB和AOF。其中,RDB持久化通过介绍RDB文件结构、触发持久化的机制和过程来讲解。包括:save同步模式、bgsave同步模式和自动配置模式。针对AOF持久化,介绍了AOF持久化过程、缓冲区同步文件策略和AOF重写机制。缓冲区同步策略包括:always策略、everysec策略和no策略。