微信搜寻公众号“码路印记”
本文将从以下几个方面介绍Redis长久化机制:
## 写在后面
本文从整体上具体介绍Redis的两种长久化形式,蕴含工作原理、长久化流程及实际策略,以及背地的一些理论知识。上一篇文章仅介绍了RDB长久化,然而Redis长久化是一个整体,独自介绍不成体系,故重新整理。
Redis是一个内存数据库,所有的数据将保留在内存中,这与传统的MySQL、Oracle、SqlServer等关系型数据库间接把数据保留到硬盘相比,Redis的读写效率十分高。然而保留在内存中也有一个很大的缺点,一旦断电或者宕机,内存数据库中的内容将会全副失落。为了补救这一缺点,Redis提供了把内存数据长久化到硬盘文件,以及通过备份文件来复原数据的性能,即Redis长久化机制。
Redis反对两种形式的长久化:RDB快照和AOF。
RDB长久化
RDB快照用官网的话来说:RDB长久化计划是依照指定工夫距离对你的数据集生成的工夫点快照(point-to-time snapshot)。它以压缩的二进制文件保留Redis数据库某一时刻所有数据对象的内存快照,可用于Redis的数据备份、转移与复原。到目前为止,仍是官网的默认反对计划。
RDB工作原理
既然说RDB是Redis中数据集的工夫点快照,那咱们先简略理解一下Redis内的数据对象在内存中是如何存储与组织的。
默认状况下,Redis中有16个数据库,编号从0-15,每个Redis数据库应用一个redisDb
对象来示意,redisDb
应用hashtable存储K-V对象。为不便了解,我以其中一个db为例绘制Redis外部数据的存储构造示意图。
工夫点快照也就是某一时刻Redis内每个DB中每个数据对象的状态,先假如在这一时刻所有的数据对象不再扭转,咱们就能够依照上图中的数据结构关系,把这些数据对象顺次读取进去并写入到文件中,以此实现Redis的长久化。而后,当Redis重启时依照规定读取这个文件中的内容,再写入到Redis内存即可复原至长久化时的状态。
当然,这个前提时咱们下面的假如成立,否则面对一个时刻变动的数据集,咱们无从下手。咱们晓得Redis中客户端命令解决是单线程模型,如果把长久化作为一个命令解决,那数据集必定时处于静止状态。另外,操作系统提供的fork()函数创立的子过程可取得与父过程统一的内存数据,相当于获取了内存数据正本;fork实现后,父过程该干嘛干嘛,长久化状态的工作交给子过程就行了。
很显然,第一种状况不可取,长久化备份会导致短时间内Redis服务不可用,这对于高HA的零碎来讲是无奈容忍的。所以,第二种形式是RDB长久化的次要实际形式。因为fork子过程后,父过程数据始终在变动,子过程并不与父进程同步,RDB长久化必然无奈保障实时性;RDB长久化实现后产生断电或宕机,会导致局部数据失落;备份频率决定了失落数据量的大小,进步备份频率,意味着fork过程耗费较多的CPU资源,也会导致较大的磁盘I/O。
长久化流程
在Redis内实现RDB长久化的办法有rdbSave和rdbSaveBackground两个函数办法(源码文件rdb.c中),先简略说下两者差异:
- rdbSave:是同步执行的,办法调用后就会立即启动长久化流程。因为Redis是单线程模型,长久化过程中会阻塞,Redis无奈对外提供服务;
- rdbSaveBackground:是后盾(异步)执行的,该办法会fork出子过程,真正的长久化过程是在子过程中执行的(调用rdbSave),主过程会持续提供服务;
RDB长久化的触发必然离不开以上两个办法,触发的形式分为手动和主动。手动触发容易了解,是指咱们通过Redis客户端人为的对Redis服务端发动长久化备份指令,而后Redis服务端开始执行长久化流程,这里的指令有save和bgsave。主动触发是Redis依据本身运行要求,在满足预设条件时主动触发的长久化流程,主动触发的场景有如下几个(摘自这篇文章):
- serverCron中
save m n
配置规定主动触发; - 从节点全量复制时,主节点发送rdb文件给从节点实现复制操作,主节点会登程bgsave;
- 执行
debug reload
命令从新加载redis时; - 默认状况下(未开启AOF)执行shutdown命令时,主动执行bgsave;
联合源码及参考文章,我整顿了RDB长久化流程来帮忙大家有个整体的理解,而后再从一些细节进行阐明。
从上图能够晓得:
- 主动触发的RDB长久化是通过rdbSaveBackground以子过程形式执行的长久化策略;
- 手动触发是以客户端命令形式触发的,蕴含save和bgsave两个命令,其中save命令是在Redis的命令解决线程以阻塞的形式调用
rdbSave
办法实现的。
主动触发流程是一个残缺的链路,涵盖了rdbSaveBackground、rdbSave等,接下来我以serverCron为例剖析一下整个流程。
save规定及查看
serverCron是Redis内的一个周期性函数,每隔100毫秒执行一次,它的其中一项工作就是:依据配置文件中save规定来判断以后须要进行主动长久化流程,如果满足条件则尝试开始长久化。理解一下这部分的实现。
在redisServer
中有几个与RDB长久化无关的字段,我从代码中摘出来,中英文对照着看下:
struct redisServer { /* 省略其余字段 */ /* RDB persistence */ long long dirty; /* Changes to DB from the last save * 上次长久化后批改key的次数 */ struct saveparam *saveparams; /* Save points array for RDB, * 对应配置文件多个save参数 */ int saveparamslen; /* Number of saving points, * save参数的数量 */ time_t lastsave; /* Unix time of last successful save * 上次长久化工夫*/ /* 省略其余字段 */}/* 对应redis.conf中的save参数 */struct saveparam { time_t seconds; /* 统计工夫范畴 */ int changes; /* 数据批改次数 */};
saveparams
对应redis.conf
下的save规定,save参数是Redis触发主动备份的触发策略,seconds
为统计工夫(单位:秒), changes
为在统计工夫内产生写入的次数。save m n
的意思是:m秒内有n条写入就触发一次快照,即备份一次。save参数能够配置多组,满足在不同条件的备份要求。如果须要敞开RDB的主动备份策略,能够应用save ""
。以下为几种配置的阐明:
# 示意900秒(15分钟)内至多有1个key的值发生变化,则执行save 900 1# 示意300秒(5分钟)内至多有1个key的值发生变化,则执行save 300 10# 示意60秒(1分钟)内至多有10000个key的值发生变化,则执行save 60 10000# 该配置将会敞开RDB形式的长久化save ""
serverCron
对RDB save规定的检测代码如下所示:
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { /* 省略其余逻辑 */ /* 如果用户申请进行AOF文件重写时,Redis正在执行RDB长久化,Redis会安顿在RDB长久化实现后执行AOF文件重写, * 如果aof_rewrite_scheduled为true,阐明须要执行用户的申请 */ /* Check if a background saving or AOF rewrite in progress terminated. */ if (hasActiveChildProcess() || ldbPendingChildren()) { run_with_period(1000) receiveChildInfo(); checkChildrenDone(); } else { /* 后盾无 saving/rewrite 子过程才会进行,一一查看每个save规定*/ for (j = 0; j < server.saveparamslen; j++) { struct saveparam *sp = server.saveparams+j; /* 查看规定有几个:满足批改次数,满足统计周期,达到重试工夫距离或者上次长久化实现*/ if (server.dirty >= sp->changes && server.unixtime-server.lastsave > sp->seconds &&(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY || server.lastbgsave_status == C_OK)) { serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...", sp->changes, (int)sp->seconds); rdbSaveInfo rsi, *rsiptr; rsiptr = rdbPopulateSaveInfo(&rsi); /* 执行bgsave过程 */ rdbSaveBackground(server.rdb_filename,rsiptr); break; } } /* 省略:Trigger an AOF rewrite if needed. */ } /* 省略其余逻辑 */}
如果没有后盾的RDB长久化或AOF重写过程,serverCron会依据以上配置及状态判断是否须要执行长久化操作,判断根据就是看lastsave、dirty是否满足saveparams数组中的其中一个条件。如果有一个条件匹配,则调用rdbSaveBackground办法,执行异步长久化流程。
rdbSaveBackground
rdbSaveBackground是RDB长久化的辅助性办法,次要工作是fork子过程,而后依据调用方(父过程或者子过程)不同,有两种不同的执行逻辑。
- 如果调用方是父过程,则fork出子过程,保留子过程信息后间接返回。
- 如果调用方是子过程则调用rdbSave执行RDB长久化逻辑,长久化实现后退出子过程。
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) { pid_t childpid; if (hasActiveChildProcess()) return C_ERR; server.dirty_before_bgsave = server.dirty; server.lastbgsave_try = time(NULL); // fork子过程 if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) { int retval; /* Child 子过程:批改过程题目 */ redisSetProcTitle("redis-rdb-bgsave"); redisSetCpuAffinity(server.bgsave_cpulist); // 执行rdb长久化 retval = rdbSave(filename,rsi); if (retval == C_OK) { sendChildCOWInfo(CHILD_TYPE_RDB, 1, "RDB"); } // 长久化实现后,退出子过程 exitFromChild((retval == C_OK) ? 0 : 1); } else { /* Parent 父过程:记录fork子过程的工夫等信息*/ if (childpid == -1) { server.lastbgsave_status = C_ERR; serverLog(LL_WARNING,"Can't save in background: fork: %s", strerror(errno)); return C_ERR; } serverLog(LL_NOTICE,"Background saving started by pid %ld",(long) childpid); // 记录子过程开始的工夫、类型等。 server.rdb_save_time_start = time(NULL); server.rdb_child_type = RDB_CHILD_TYPE_DISK; return C_OK; } return C_OK; /* unreached */}
rdbSave是真正执行长久化的办法,它在执行时存在大量的I/O、计算操作,耗时、CPU占用较大,在Redis的单线程模型中长久化过程会继续占用线程资源,进而导致Redis无奈提供其余服务。为了解决这一问题Redis在rdbSaveBackground中fork出子过程,由子过程实现长久化工作,防止了占用父过程过多的资源。
须要留神的是,如果父过程内存占用过大,fork过程会比拟耗时,在这个过程中父过程无奈对外提供服务;另外,须要综合思考计算机内存使用量,fork子过程后会占用双倍的内存资源,须要确保内存够用。通过info stats命令查看latest_fork_usec选项,能够获取最近一个fork以操作的耗时。
rdbSave
Redis的rdbSave函数是真正进行RDB长久化的函数,流程、细节贼多,整体流程能够总结为:创立并关上临时文件、Redis内存数据写入临时文件、临时文件写入磁盘、临时文件重命名为正式RDB文件、更新长久化状态信息(dirty、lastsave)。其中“Redis内存数据写入临时文件”最为外围和简单,写入过程间接体现了RDB文件的文件格式,本着一图胜千言的理念,我依照源码流程绘制了下图。
补充阐明一下,上图右下角“遍历以后数据库的键值对并写入”这个环节会依据不同类型的Redis数据类型及底层数据结构采纳不同的格局写入到RDB文件中,不再开展了。我感觉大家对整个过程有个直观的了解就好,这对于咱们了解Redis外部的运作机制大有裨益。
AOF长久化
上一节咱们晓得RDB是一种工夫点(point-to-time)快照,适宜数据备份及劫难复原,因为工作原理的“先天性缺点”无奈保障实时性长久化,这对于缓存失落零容忍的零碎来说是个硬伤,于是就有了AOF。
AOF工作原理
AOF是Append Only File的缩写,它是Redis的齐全长久化策略,从1.1版本开始反对;这里的file存储的是引起Redis数据批改的命令汇合(比方:set/hset/del等),这些汇合依照Redis Server的解决程序追加到文件中。当重启Redis时,Redis就能够从头读取AOF中的指令并重放,进而复原敞开前的数据状态。
AOF长久化默认是敞开的,批改redis.conf以下信息并重启,即可开启AOF长久化性能。
# no-敞开,yes-开启,默认noappendonly yesappendfilename appendonly.aof
AOF实质是为了长久化,长久化对象是Redis内每一个key的状态,长久化的目标是为了在Reids产生故障重启后可能复原至重启前或故障前的状态。相比于RDB,AOF采取的策略是依照执行程序长久化每一条可能引起Redis中对象状态变更的命令,命令是有序的、有抉择的。把aof文件转移至任何一台Redis Server,从头到尾按序重放这些命令即可复原如初。举个例子:
首先执行指令set number 0
,而后随机调用incr number
、get number
各5次,最初再执行一次get number
,咱们失去的后果必定是5。
因为在这个过程中,可能引起number
状态变更的只有set/incr
类型的指令,并且它们执行的先后顺序是已知的,无论执行多少次get
都不会影响number
的状态。所以,保留所有set/incr
命令并长久化至aof文件即可。依照aof的设计原理,aof文件中的内容应该是这样的(这里是假如,理论为RESP协定):
set number 0incr numberincr numberincr numberincr numberincr number
最实质的原理用“命令重放”四个字就能够概括。然而,思考理论生产环境的复杂性及操作系统等方面的限度,Redis所要思考的工作要比这个例子简单的多:
- Redis Server启动后,aof文件始终在追加命令,文件会越来越大。文件越大,Redis重启后复原耗时越久;文件太大,转移工作就越难;不加治理,可能撑爆硬盘。很显然,须要在适合的机会对文件进行精简。例子中的5条incr指令很显著的能够替换为为一条
set
命令,存在很大的压缩空间。 - 家喻户晓,文件I/O是操作系统性能的短板,为了提高效率,文件系统设计了一套简单的缓存机制,Redis操作命令的追加操作只是把数据写入了缓冲区(aof_buf),从缓冲区到写入物理文件在性能与平安之间衡量会有不同的抉择。
- 文件压缩即意味着重写,重写时即可根据已有的aof文件做命令整合,也能够先依据以后Redis内数据的状态做快照,再把存储快照过程中的新增的命令做追加。
- aof备份后的文件是为了复原数据,联合aof文件的格局、完整性等因素,Redis也要设计一套残缺的计划做反对。
长久化流程
从流程上来看,AOF的工作原理能够概括为几个步骤:命令追加(append)、文件写入与同步(fsync)、文件重写(rewrite)、重启加载(load),接下来顺次理解每个步骤的细节及背地的设计哲学。
命令追加
当 AOF 长久化性能处于关上状态时,Redis 在执行完一个写命令之后,会以协定格局(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )把被执行的写命令追加到 Redis 服务端保护的 AOF 缓冲区开端。对AOF文件只有单线程的追加操作,没有seek等简单的操作,即便断电或宕机也不存在文件损坏危险。另外,应用文本协定好处多多:
- 文本协定有很好的兼容性;
- 文本协定就是客户端的申请命令,不须要二次解决,节俭了存储及加载时的解决开销;
- 文本协定具备可读性,不便查看、批改等解决。
AOF缓冲区类型为Redis自主设计的数据结构sds
,Redis会依据命令的类型采纳不同的办法(catAppendOnlyGenericCommand
、catAppendOnlyExpireAtCommand
等)对命令内容进行解决,最初写入缓冲区。
须要留神的是:如果命令追加时正在进行AOF重写,这些命令还会追加到重写缓冲区(aof_rewrite_buffer
)。
文件写入与同步
AOF文件的写入与同步离不开操作系统的反对,开始介绍之前,咱们须要补充一下Linux I/O缓冲区相干常识。硬盘I/O性能较差,文件读写速度远远比不上CPU的处理速度,如果每次文件写入都期待数据写入硬盘,会整体拉低操作系统的性能。为了解决这个问题,操作系统提供了提早写(delayed write)机制来进步硬盘的I/O性能。
传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存,大多数磁盘I/O都通过缓冲进行。 当将数据写入文件时,内核通常先将该数据复制到其中一个缓冲区中,如果该缓冲区尚未写满,则并不将其排入输入队列,而是期待其写满或者当内核须要重用该缓冲区以便寄存其余磁盘块数据时, 再将该缓冲排入到输入队列,而后待其达到队首时,才进行理论的I/O操作。这种输入形式就被称为提早写。
提早写缩小了磁盘读写次数,然而却升高了文件内容的更新速度,使得欲写到文件中的数据在一段时间内并没有写到磁盘上。当零碎产生故障时,这种提早可能造成文件更新内容的失落。为了保障磁盘上理论文件系统与缓冲区高速缓存中内容的一致性,UNIX零碎提供了sync、fsync和fdatasync三个函数为强制写入硬盘提供反对。
Redis每次事件轮训完结前(beforeSleep
)都会调用函数flushAppendOnlyFile
,flushAppendOnlyFile
会把AOF缓冲区(aof_buf
)中的数据写入内核缓冲区,并且依据appendfsync
配置来决定采纳何种策略把内核缓冲区中的数据写入磁盘,即调用fsync()
。该配置有三个可选项always
、no
、everysec
,具体阐明如下:
- always:每次都调用
fsync()
,是安全性最高、性能最差的一种策略。 - no:不会调用
fsync()
。性能最好,安全性最差。 - everysec:仅在满足同步条件时调用
fsync()
。这是官网倡议的同步策略,也是默认配置,做到兼顾性能和数据安全性,实践上只有在零碎忽然宕机的状况下失落1秒的数据。
留神:下面介绍的策略受配置项no-appendfsync-on-rewrite
的影响,它的作用是告知Redis:AOF文件重写期间是否禁止调用fsync(),默认是no。
如果appendfsync
设置为always
或everysec
,后盾正在进行的BGSAVE
或者BGREWRITEAOF
耗费过多的磁盘I/O,在某些Linux系统配置下,Redis对fsync()的调用可能阻塞很长时间。然而这个问题还没有修复,因为即便是在不同的线程中执行fsync()
,同步写入操作也会被阻塞。
为了缓解此问题,能够应用该选项,以避免在进行BGSAVE
或BGREWRITEAOF
时在主过程中调用fsync()。
- 设置为
yes
意味着,如果子过程正在进行BGSAVE
或BGREWRITEAOF
,AOF的长久化能力就与appendfsync
设置为no
有着雷同的成果。最蹩脚的状况下,这可能会导致30秒的缓存数据失落。 - 如果你的零碎有下面形容的提早问题,就把这个选项设置为
yes
,否则放弃为no
。
文件重写
如后面提到的,Redis长时间运行,命令一直写入AOF,文件会越来越大,不加管制可能影响宿主机的平安。
为了解决AOF文件体积问题,Redis引入了AOF文件重写性能,它会依据Redis内数据对象的最新状态生成新的AOF文件,新旧文件对应的数据状态统一,然而新文件会具备较小的体积。重写既缩小了AOF文件对磁盘空间的占用,又能够进步Redis重启时数据恢复的速度。还是上面这个例子,旧文件中的6条命令等同于新文件中的1条命令,压缩成果不言而喻。
咱们说,AOF文件太大时会触发AOF文件重写,那到底是多大呢?有哪些状况会触发重写操作呢?
**
与RDB形式一样,AOF文件重写既能够手动触发,也会主动触发。手动触发间接调用bgrewriteaof
命令,如果过后无子过程执行会立即执行,否则安顿在子过程完结后执行。主动触发由Redis的周期性办法serverCron
查看在满足肯定条件时触发。先理解两个配置项:
- auto-aof-rewrite-percentage:代表以后AOF文件大小(aof_current_size)和上一次重写后AOF文件大小(aof_base_size)相比,增长的比例。
- auto-aof-rewrite-min-size:示意运行
BGREWRITEAOF
时AOF文件占用空间最小值,默认为64MB;
Redis启动时把aof_base_size
初始化为过后aof文件的大小,Redis运行过程中,当AOF文件重写操作实现时,会对其进行更新;aof_current_size
为serverCron
执行时AOF文件的实时大小。当满足以下两个条件时,AOF文件重写就会触发:
增长比例:(aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage文件大小:aof_current_size > auto-aof-rewrite-min-size
手动触发与主动触发的代码如下,同样在周期性办法serverCron
中:
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { /* 省略其余逻辑 */ /* 如果用户申请进行AOF文件重写时,Redis正在执行RDB长久化,Redis会安顿在RDB长久化实现后执行AOF文件重写, * 如果aof_rewrite_scheduled为true,阐明须要执行用户的申请 */ if (!hasActiveChildProcess() && server.aof_rewrite_scheduled) { rewriteAppendOnlyFileBackground(); } /* Check if a background saving or AOF rewrite in progress terminated. */ if (hasActiveChildProcess() || ldbPendingChildren()) { run_with_period(1000) receiveChildInfo(); checkChildrenDone(); } else { /* 省略rdb长久化条件查看 */ /* AOF重写条件查看:aof开启、无子过程运行、增长百分比已设置、以后文件大小超过阈值 */ if (server.aof_state == AOF_ON && !hasActiveChildProcess() && server.aof_rewrite_perc && server.aof_current_size > server.aof_rewrite_min_size) { long long base = server.aof_rewrite_base_size ? server.aof_rewrite_base_size : 1; /* 计算增长百分比 */ long long growth = (server.aof_current_size*100/base) - 100; if (growth >= server.aof_rewrite_perc) { serverLog(LL_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth); rewriteAppendOnlyFileBackground(); } } } /**/}
AOF文件重写的流程是什么?据说Redis反对混合长久化,对AOF文件重写有什么影响?
从4.0版本开始,Redis在AOF模式中引入了混合长久化计划,即:纯AOF形式、RDB+AOF形式,这一策略由配置参数aof-use-rdb-preamble
(应用RDB作为AOF文件的前半段)管制,默认敞开(no),设置为yes可开启。所以,在AOF重写过程中文件的写入会有两种不同的形式。当aof-use-rdb-preamble
的值是:
- no:依照AOF格局写入命令,与4.0前版本无差别;
- yes:先依照RDB格局写入数据状态,而后把重写期间AOF缓冲区的内容以AOF格局写入,文件前半部分为RDB格局,后半部分为AOF格局。
联合源码(6.0版本,源码太多这里不贴出,可参考aof.c
)及参考资料,绘制AOF重写(BGREWRITEAOF)流程图:
联合上图,总结一下AOF文件重写的流程:
- rewriteAppendOnlyFileBackground开始执行,查看是否有正在进行的AOF重写或RDB长久化子过程:如果有,则退出该流程;如果没有,则持续创立接下来父子过程间数据传输的通信管道。执行fork()操作,胜利后父子过程别离执行不同的流程。
父过程:
- 记录子过程信息(pid)、工夫戳等;
- 持续响应其余客户端申请;
- 收集AOF重写期间的命令,追加至aof_rewrite_buffer;
- 期待并向子进程同步aof_rewrite_buffer的内容;
子过程:
- 批改以后过程名称,创立重写所需的临时文件,调用rewriteAppendOnlyFile函数;
- 依据
aof-use-rdb-preamble
配置,以RDB或AOF形式写入前半部分,并同步至硬盘; - 从父过程接管增量AOF命令,以AOF形式写入后半局部,并同步至硬盘;
- 重命名AOF文件,子过程退出。
数据加载
Redis启动后通过loadDataFromDisk
函数执行数据加载工作。这里须要留神,尽管长久化形式能够抉择AOF、RDB或者两者兼用,然而数据加载时必须做出抉择,两种形式各自加载一遍就乱套了。
实践上,AOF长久化比RDB具备更好的实时性,当开启了AOF长久化形式,Redis在数据加载时优先思考AOF形式。而且,Redis 4.0版本后AOF反对了混合长久化,加载AOF文件须要思考版本兼容性。Redis数据加载流程如下图所示:
在AOF形式下,开启混合长久化机制生成的文件是“RDB头+AOF尾”,未开启时生成的文件全副为AOF格局。思考两种文件格式的兼容性,如果Redis发现AOF文件为RDB头,会应用RDB数据加载的办法读取并复原前半部分;而后再应用AOF形式读取并复原后半局部。因为AOF格局存储的数据为RESP协定命令,Redis采纳伪客户端执行命令的形式来复原数据。
如果在AOF命令追加过程中产生宕机,因为提早写的技术特点,AOF的RESP命令可能不残缺(被截断)。遇到这种状况时,Redis会依照配置项aof-load-truncated
执行不同的解决策略。这个配置是通知Redis启动时读取aof文件,如果发现文件被截断(不残缺)时该如何解决:
- yes:则尽可能多的加载数据,并以日志的形式告诉用户;
- no:则以零碎谬误的形式解体,并禁止启动,须要用户修复文件后再重启。
总结
Redis提供了两种长久化的抉择:RDB反对以特定的实际距离为数据集生成工夫点快照;AOF把Redis Server收到的每条写指令长久化到日志中,待Redis重启时通过重放命令复原数据。日志格局为RESP协定,对日志文件只做append操作,无损坏危险。并且当AOF文件过大时能够主动重写压缩文件。
当然,如果你不须要对数据进行长久化,也能够禁用Redis的长久化性能,然而大多数状况并非如此。实际上,咱们时有可能同时应用RDB和AOF两种形式的,最重要的就是咱们要了解两者的区别,以便正当应用。
RDB vs AOF
RDB长处
- RDB是一个紧凑压缩的二进制文件,代表Redis在某一个工夫点上的数据快照,非常适合用于备份、全量复制等场景。
- RDB对劫难复原、数据迁徙十分敌对,RDB文件能够转移至任何须要的中央并从新加载。
- RDB是Redis数据的内存快照,数据恢复速度较快,相比于AOF的命令重放有着更高的性能。
RDB毛病
- RDB形式无奈做到实时或秒级长久化。因为长久化过程是通过fork子过程后由子过程实现的,子过程的内存只是在fork操作那一时刻父过程的数据快照,而fork操作后父过程继续对外服务,外部数据时刻变更,子过程的数据不再更新,两者始终存在差别,所以无奈做到实时性。
- RDB长久化过程中的fork操作,会导致内存占用加倍,而且父过程数据越多,fork过程越长。
- Redis申请高并发可能会频繁命中save规定,导致fork操作及长久化备份的频率不可控;
- RDB文件有文件格式要求,不同版本的Redis会对文件格式进行调整,存在老版本无奈兼容新版本的问题。
AOF长处
- AOF长久化有更好的实时性,咱们能够抉择三种不同的形式(appendfsync):no、every second、always,every second作为默认的策略具备最好的性能,极其状况下可能会失落一秒的数据。
- AOF文件只有append操作,无简单的seek等文件操作,没有损坏危险。即便最初写入数据被截断,也很容易应用
redis-check-aof
工具修复; - 当AOF文件变大时,Redis可在后盾主动重写。重写过程中旧文件会继续写入,重写实现后新文件将变得更小,并且重写过程中的增量命令也会append到新文件。
- AOF文件以已于了解与解析的形式蕴含了对Redis中数据的所有操作命令。即便不小心谬误的革除了所有数据,只有没有对AOF文件重写,咱们就能够通过移除最初一条命令找回所有数据。
- AOF曾经反对混合长久化,文件大小能够无效管制,并进步了数据加载时的效率。
AOF毛病
- 对于雷同的数据汇合,AOF文件通常会比RDB文件大;
- 在特定的fsync策略下,AOF会比RDB略慢。一般来讲,fsync_every_second的性能依然很高,fsync_no的性能与RDB相当。然而在微小的写压力下,RDB更能提供最大的低延时保障。
- 在AOF上,Redis已经遇到一些简直不可能在RDB上遇到的常见bug。一些非凡的指令(如BRPOPLPUSH)导致从新加载的数据与长久化之前不统一,Redis官网已经在雷同的条件下进行测试,然而无奈复现问题。
应用倡议
对RDB和AOF两种长久化形式的工作原理、执行流程及优缺点理解后,咱们来思考下,理论场景中应该怎么权衡利弊,正当的应用两种长久化形式。如果仅仅是应用Redis作为缓存工具,所有数据能够依据长久化数据库进行重建,则可敞开长久化性能,做好预热、缓存穿透、击穿、雪崩之类的防护工作即可。
个别状况下,Redis会承当更多的工作,如分布式锁、排行榜、注册核心等,长久化性能在劫难复原、数据迁徙方面将施展较大的作用。倡议遵循几个准则:
- 不要把Redis作为数据库,所有数据尽可能可由应用服务主动重建。
- 应用4.0以上版本Redis,应用AOF+RDB混合长久化性能。
- 正当布局Redis最大占用内存,避免AOF重写或save过程中资源有余。
- 防止单机部署多实例。
- 生产环境多为集群化部署,可在slave开启长久化能力,让master更好的对外提供写服务。
- 备份文件应主动上传至异地机房或云存储,做好劫难备份。
对于fork()
通过下面的剖析,咱们都晓得RDB的快照、AOF的重写都须要fork,这是一个重量级操作,会对Redis造成阻塞。因而为了不影响Redis主过程响应,咱们须要尽可能升高阻塞。
- 升高fork的频率,比方能够手动来触发RDB生成快照、与AOF重写;
- 管制Redis最大应用内存,避免fork耗时过长;
- 应用更高性能的硬件;
- 合理配置Linux的内存调配策略,防止因为物理内存不足导致fork失败。
参考文献
- 《一文看懂Redis的长久化原理》 https://juejin.im/post/5b70dfcf518825610f1f5c16
- 《redis 长久化详解,RDB和AOF是什么?他们优缺点是什么?运行流程是什么?》 http://www.chenxm.cc/article/38.html
- 《几率大的Redis面试题(含答案)》 https://blog.csdn.net/Butterfly_resting/article/details/89668661
- 《史上最全Redis面试题(含答案):哨兵+复制+事务+集群+长久化等》https://blog.csdn.net/qq_41699100/article/details/86102235
- 《Redis长久化机制》https://juejin.im/post/5d8587c65188253a4835306b
- 《Reids Persistence》 https://redis.io/topics/persistence。
- 《Redis RDB Persistence Details》https://programming.vip/docs/redis-rdb-persistence-details.html