Redis(1.4)Redis的持久化
Redis持久化
【1】概念
Redis所有的数据存储在内存中,为了保证重启后,redis数据不丢失,需要把redis数据保存在磁盘中。
【2】持久化使用方式策略
(1)RDB 方式:默认支持,不需要配置
在指定的时间间隔内,将redis 内存中的数据集快照写入到磁盘当中去,比如每隔30S写入一次。
(2)AOF方式
以日志的形式记录服务器所处理的的每一个操作,在服务器启动之初,会读取该文件来重新构建redis数据库数据,以此来保证重启后数据库的数据是完整的。
(3)不持久化
单纯做缓存用
(4)RDB+AOF 同时使用
【3】两种核心方法
(1)RDB
优势:
(1.1)整个redis数据库只包含1个文件
(1.2)对于灾难恢复而言,可以通过拷贝压缩等传输到异地机
(1.3)性能最大化:在开始持久化的时候,只需要分一些进程,接下来就可以由子进程完成持计划的工作。
可以避免服务器进程进行持久化操作。数据量大的时候,恢复速度很快。
劣势:
(1.1)对于高可用性和数据安全性而言:会丢失一个间隔内的数据,比如30秒一次持久化,但28秒的时候宕机了,那么就会丢自上一个备份后的28秒的所有操作。
(1.2)当数据集非常大的时候,需要服务器停止一点时间甚至超过1s
基本操作与配置:
(1.1)默认的存盘配置如下(大概在文档15%的位置)
如英文描述一样。
save num1_second num2_key 每num1秒中,有num2个key有修改,则存盘
这里的默认就是每900/300/60 秒 有 1/10/10000 个 key 被修改,就存盘。
(1.2)默认redis数据库文件名称与存放路径(大概在文档15%的位置)
上面是文件名,下面是上面文件所在的路径, ./的意思就是,和当前打开的配置文件同一路径。
(2)AOF
优势:
(2.1)3种同步策略
《1》每秒同步:异步持计划,效率高,会丢失1S内数据
《2》每修改同步:同步持久化,效率低,不会丢数据
《3》不同步
(2.2)日志文件
《1》文件写入形式:使用的是append追加方式,如果宕机不会被破坏已经写入的数据。
《2》宕机恢复:如果一个命令写到一般就宕机了,可以用 reids-check-aof 工具来解决数据一致性问题。
《3》重写机制:如果日志文件过大,redis可以自动启用重写重写机制.
把旧的比如 del key1 、hdel key2 等无效记录给清理掉,set key1 、set key2等操作也可以合并成 mset,保留能重做出现在数据的最精简记录。
同时redis还会创建一个新的文件来记录此期间哪些修改命令被执行了。因此在进行重写切换的时候,可以更好的保证数据那权限。
参数:
no-appendfsync-on-rewrite no #是否在重写的时候 停止追加命令,no为不停止,依然会新建一个文件来记录重写期间的命令
auto-aof-rewrite-min-size 64mb #重写文件的最小文件大小,这里是重写的文件必须要大于64M以上才开始重写
auto-aof-rewrite-percentage 100 #重写文件的百分比阀值,这里写的是文件增长100%大小 才重写
《4》可读性强:日志文件记录了很详细的修改操作记录和数据,可以用此文件重建数据库。
劣势
(2.1)文件大小:AOF比RDB文件大许多
(2.2)执行效率:每次操作都需要记录,比起RDB每过一段时间批量持久化记录,要慢不少。
(2.3)需要额外配置:默认是关闭的,如果要启用则要把下图中第1行改成 yes,开启后下面就是文件名。
基本操作与配置:
(2.1)开启:
最后面的3行是同步策略,always 是修改后就立马同步,everysec 是每一秒,另外一个是不同步
(2.2)修改配置:进行配置放开测试,修改如下图(要想生效,需要重启redis)
(2.3)重启redis后查看文件:
就可以发现文件了(默认路径是和conf文件在同一目录下)
(2.4)模拟通过aof文件恢复
《1》删掉所有key:flushall -- 可以刷掉所有key
《2》关掉redis :./redis-cli shutdown
《3》编辑aof文件:vim ./appendonly.aof
对比一下我们的输入命令和aof文件记录的命令;
把 flushall 在aof 文件里删掉,就可以用 aof 文件来重做恢复我们的数据库数据。(redis 会自动加载 aof 文件的数据操作)
《4》启动redis -》连接 redis =》查看key是否存在
《5》修复 aof文件(开启错误信息日志文件)
故障模拟操作:当我没有关系redis 服务端的时候,我去修改了 aof文件
重新启动,不报任何错误,但用 ps -ef|grep redis 发现进程没有起来。
如何获取报错信息呢?修改 redis.conf 文件,找到logfile 字样,这里直接写名字的话默认路径是在配置文件相同目录下。也可以写URL
这里原本值是"",我改成了如下图。
再次启动服务,查看错误日志文件。报错信息如下
需要我们修复 aof 文件之后,达成了一致性,才可以启动成功。
./redis-check-aof --fix ./appendonly.aof
修复后然后再次启动,核验启动成功
【4】RDB原理
(4.1)rdb的优缺点
1、rdb文件
rdb文件都是二进制,很小。比如内存数据有10gb,rdb文件可能就1gb,只是举例。
2、优点
由于rdb文件都是二进制文件,所以很小,在灾难恢复的时候会快些。
他的效率(主进程处理命令的效率,而不是持久化的效率)相对于aof要高(bgsave而不是save),因为每来个请求他都不会处理任何事,只是bgsave的时候他会fork()子进程且可能copyonwrite;
但copyonwrite只是一个寻址的过程,纳秒级别的。而aof每次都是写盘操作,毫秒级别。没法比。
3、缺点
数据可靠性比aof低,也就是会丢失的多。
因为aof可以配置每秒都持久化或者每个命令处理完就持久化一次这种高频率的操作,而rdb的话虽然也是靠配置进行bgsave,但是没有aof配置那么灵活,也没aof持久化快,因为rdb每次全量,aof每次只追加。
(4.2)RDB持久化的两种方法(save,bgsave)
配置文件也可以配置触发rdb的规则。配置文件配置的规则采取的是bgsave的原理。
1、save
同步、阻塞
缺点:
致命的问题,持久化的时候redis服务阻塞(准确的说会阻塞当前执行save命令的线程,但是redis是单线程的,所以整个服务会阻塞),不能继对外提供请求,GG!
数据量小的话肯定影响不大,数据量大呢?每次复制需要1小时,那就相当于停机一小时。
2、bgsave
异步、非阻塞
原理:
fork() + copyonwrite
优点:
他可以一边进行持久化,一边对外提供读写服务,互不影响,新写的数据对我持久化不会造成数据影响;
你持久化的过程中报错或者耗时太久都对我当前对外提供请求的服务不会产生任何影响。持久化完会将新的rdb文件覆盖之前的。
(4.3)fork()
bgsave原理是fork() + copyonwrite,那么现在来聊一下fork()
1、fork()是什么
fork()是unix和linux这种操作系统的一个api,而不是Redis的api。
2、fork()有什么用
fork()用于创建一个子进程,注意是子进程,不是子线程。
fork()出来的进程共享其父类的内存数据。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见。
比如:A进程fork()了一个子进程B,那么A进程就称之为主进程,这时候主进程子进程所指向的内存空间是同一个,所以他们的数据一致。
但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。
而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。
这一点有点像守护线程的概念。Redis正是巧妙的运用了fork()这个牛逼的api来完成RDB的持久化操作。
3、Redis中的fork()
Redis巧妙的运用了fork()。
当bgsave执行时,Redis主进程会判断当前是否有fork()出来的子进程,若有则忽略,若没有则会fork()出一个子进程来执行rdb文件持久化的工作;
子进程与Redis主进程共享同一份内存空间,所以子进程可以搞他的rdb文件持久化工作,主进程又能继续他的对外提供服务,二者互不影响。
我们说了他们之后的修改内存数据对彼此不可见,但是明明指向的都是同一块内存空间,这是咋搞得?
肯定不可能是fork()出来子进程后顺带复制了一份数据出来,如果是这样的话比如我有4g内存,那么其实最大有限空间是2g,我要给rdb留出一半空间来,扯淡一样!那他咋做的?采取了copyonwrite技术。
(4.4)redis中的 copyonwrite
很简单,现在不就是主进程和子进程共享了一块内存空间,怎么做到的彼此更改互不影响吗?
1、原理
主进程fork()子进程之后,内核把主进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向主进程。
这也就是共享了主进程的内存,当其中某个进程写内存时(这里肯定是主进程写,因为子进程只负责rdb文件持久化工作,不参与客户端的请求),CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。中断例程中,内核就会把触发的异常的页复制一份(这里仅仅复制异常页,也就是所修改的那个数据页,而不是内存中的全部数据),于是主子进程各自持有独立的一份。
数据修改之前的样子
数据修改之后的样子
2、回到原问题
其实就是更改数据的之前进行copy一份更改数据的数据页出来,比如主进程收到了set k 1请求(之前k的值是2);
然后这同时又有子进程在rdb持久化,那么主进程就会把k这个key的数据页拷贝一份,并且主进程中k这个指针指向新拷贝出来的数据页地址上,然后进行更改值为1的操作;
这个主进程k元素地址引用的新拷贝出来的地址,而子进程引用的内存数据k还是修改之前的。
3、一段话总结
copyonwritefork()出来的子进程共享主进程的物理空间,当主子进程有内存写入操作时,read-only内存页发生中断,将触发的异常的内存页复制一份(其余的页还是共享主进程的)。
4、额外补充
在 Redis 服务中,子进程只会读取共享内存中的数据,它并不会执行任何写操作,只有主进程会在写入时才会触发这一机制;
而对于大多数的 Redis 服务或者数据库,写请求往往都是远小于读请求的,所以使用fork()加上写时拷贝这一机制能够带来非常好的性能,也让BGSAVE这一操作的实现变得很简单。
(4.5)调用fork()也会阻塞吗
我只能说没毛病,但是这个阻塞真的可以忽略不计。尤其是相对于阻塞主线程的save。
(4.6)rdb瞬间执行多次,会同时存在多个子进程吗?
不会,主进程每次收到bgsave命令需要fork()子进程之前都会判断是否存在子进程了,若存在也会忽略掉这次bgsave请求。若不存在我会fork()出子进程进行工作。
为什么这么搞?
我猜测原因如下:
1.如果支持并行存在多个子进程,那么不仅会拉低服务器性能,还会造成数据问题,比如八点的bgsave在工作,九点又来个bgsave命令。
这时候九点的先执行完了,八点的后执行完了,那九点的不白执行了吗?这是我所谓的数据问题。再比如,都没执行完,十点又开一个bgsave,越积越多,服务器性能被拉低。
2.那为什么不阻塞?
判断有子进程在工作,就等待,等他执行完我在上场,那一样,越积越多,文件过大,只会造成堆积。
2、如果没有copyonwrite这种技术是什么效果?
1.假设是全量复制,那么内存空间直接减半,浪费资源不说,数据量10g,全量复制这10g的时间也够长的。这谁顶得住?
.如果不全量复制,会是怎样?相当于我一边复制,你一边写数据,看着貌似问题不大,其实不然。比如现在Redis里有k1的值是1,k2的值是
2,比如bgsave了,这时候rdb写入了k1的值,在写k2的值之前时,有个客户端请求
set k1 11
set k2 22
那么持久化进去的是k2 22,但是k1的值还是1,而不是最新的11,所以会造成数据问题,所以采取了copyonwrite技术来保证触发bgsave请求的时候无论你怎么更改,都对我rdb文件的数据持久化不会造成任何影响。
总结:
我们熟知redis是内存数据库,它将自己的数据存储在内存里面,如果如图redis进程退出或突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。
Redis 的持久化机制有三种,第一种是快照(RDB),第二种是 AOF 日志,第三种是混合持久化。快照是一次全量备份,AOF 日志是连续的增量备份。
快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。混合持久化综合了上面两种的优点。
【5】再浅谈RDB原理
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发
(5.1)触发机制
1.save命令:阻塞当前redis服务器,直RDB过程完成为止。(如果内存比较大会造成redis长时间阻塞,这样显然不是我们想要的。线上禁止使用)
2.bgsave命令:redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般很短(和实例数据大小有关系)
除了执行命令手动触发之外,redis内部还存在自动触发RDB的持久化机制,如下
1.使用save相关配置,如“save m n” 表示m秒内数据集存在n次修改时(可以配置多组条件,其中一个达标就触发),自动触发bgsave
2.如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点(具体复制细节见下一篇)
3.执行debug reload命令重写加载redis时,
4.默认情况下执行shutdown命令时,如果没有开启aof持久化则自动执行bgsave
bgsvae运作流程如下
1.执行bgsave命令,redis父进程判断是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回(生成RDB、AOF要浪费大量磁盘io资源,如果开启aof,磁盘io有可能成为redis的瓶颈)
2.父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通过info stats 命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时(单位ms)(具体阻塞时间和内存大小有关)
父进程fork完成后,bgsave命令返回“Background saving started”信息并不在阻塞父进程,子进程创建RDB文件,根据父进程内存生成的临时快照文件,完成后对原有的文件进行院子替换。执行lastsave命令可以获取最后一次生成RDB的时间
子进程发送信号给父进程表示完成,父进程更新统计信息
至此RDB的生成过程大概讲完了,我们知道Redis为了不阻塞主线程,调用 glibc 的函数fork产生一个子进程来生成RDB备份文件。
(5.2)Copy On Write
如果rdb期间,有个操作 delkey,那么是怎么处理的呢?
Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段.
这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。
子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。
这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成(一页4k),当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。
子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安心的遍历数据了进行序列化写磁盘了。
(5.3)RDB的载入
和使用save或者bgsave命令不同,RDB的载入是在服务器启动的时候自动执行的,所以Redis并没有专门用于载入RDB的文件命令。值得一提的是:
如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库
数据库在主从复制时候会触发RDB加载(下一篇细聊)
RDB文件的处理
保存:RDB文件保存在dir配置的指定目录下,文件名通过dbfilename配置指定。可以通过执行 config set dir{new Dir} 和 config set dbfilename{newFileName} 运行期动态执行。
压缩:redis 默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远小于内存大小,默认开启,可以通过参数config set rdbcompression{yes|no}动态修改
(5.4)RDB的优缺点
优点:
RDB是一个紧凑压缩的二进制文件,某个时间点的上的快照。适合全量复制
redis加载RDB恢复数据远快于AOF的方式
如果Redis加载损坏的RDB文件时拒绝启动,并打印如下日志:# Short read or OOM loading DB. Unrecoverable error, aborting now. 这时可以使用Redis提供的redis-check-dump工具检测RDB文件并获取对应的错误报告。
缺点:
RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。
针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。
【6】AOF原理
AOF的主要作用是解决了数据持久化的实时性,AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。
Redis 会在收到客户端修改指令后,进行参数校验进行逻辑处理后,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先执行指令才将日志存盘。
这点不同于mysql、hbase等存储引擎,它们都是先存储日志再做逻辑处理。(所以单机redis不能做到一条数据也不丢失)
(6.1)使用AOF
开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof。
保存路径同RDB持久化方式一致,通过dir配置指定。AOF的工作流程操作:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。
命令写入and文件同步
服务器在执行完一个写命令后(如 set k v,lpush k v 等),会把写入命令会追加到aof_buf(缓冲区)中。后续防止丢失aof_buf中的数据,在调用linux的glibc提供的fsync函数将aof_buf中的数据强制刷新到磁盘。
AOF为什么把命令追加到aof_buf中?Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘(调用fsync),那么性能完全取决于当前硬盘负载。
先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
(6.2)AOF策略控制
Redis提供了多种AOF缓冲区同步文件策略,由参数appendfsync控制,不同值的含义如下
always: 命令写入aof_buf后调用系统fsync操作同步到aof文件,fsync完成后线程返回(性能最差,完全取决于磁盘速度。即便如此redis也不能保证一条数据也不丢)
everysec: 命令写入aof_buf后调用系统write操作,write完成后线程返回。fsync同步文件操作由专门的线程每秒调用一次(默认配置。理论上会丢失1s的数据。(严格来说丢弃1s的数据不准确,下文有讲))
no:命令写入aof_bug后调用系统的write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期30s
系统调用write和fsync说明:
write :Linux在内核提供页缓冲区用来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
fsync : 将指定文件的内容强制从内核缓存刷到磁盘
(6.3)AOF重写机制
我们知道AOF持久化是通过保存被执行的写命令来实现的,随着命令不断写入AOF,文件会越来越大。
为了解决这个问题,Redis引入AOF重写机制压缩文件体积。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF
重写后为什么变小?
已经过期的数据不再写入文件。
旧的AOF文件含有无效命令,如set key1、del key1 。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c。
为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条
(其实这里直接遍历的是当前内存数据,如直接把一个大个的list的生成一个个lpush list a1 a2 ..a60 ,步子大了容易扯着蛋,所以默认以64个元素为一组)
更小的AOF文件可以被Redis更快的加载
AOF重写触发条件
手动触发:直接调用bgrewriteaof命令。
自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
auto-aof-rewrite-percentage(设为x):代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的百分比(如auto-aof-rewrite-percentage 100 当为两倍大小是重写)。
自动触发条件:aof_current_size > auto-aof-rewrite-min-size && (x+1)*aof_base_size
其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。
AOF重写Redis做了哪些事情呢?
AOF重写功能作为一个辅助功能,redis肯定不希望阻塞主进程的执行,所以redis把AOF重写放到一个子进程去执行。
上文讲到frok运用cow(写时复制技术)技术,所以子进程只能看到fork那一瞬间产生的镜像数据。
为了解决这一个问题redis设置了一个 AOF重写缓存区(aof_rewrite_bug) 用来存储AOF重写期间产生的命令,等子进程重写完成后通知父进程,父进程把重写缓存区的数据追加到新的AOF文件
(注:这里值得注意,AOF重写期间如果有大量的写入,父进程在把aof_rewrite_buf写到新的aof文件时会造成大量的写盘操作,会造成性能的下降,redis 4.0以后增加管道机制来优化这里(把aof_rewrite_buf追加工作交给子进程去做),感兴趣的同学可以自行查阅)
所以说在AOF重写期间主进程做了下面几件事
1.执行客户端发来的命令
2.将执行后的写命令追加到AOF缓冲区
3.将执行后的写明了追加到AOF重写缓冲区
值得注意的是,redis每次重写写入磁盘的大小由aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞
过程如图:
我们知道AOF重写期间会消耗大量磁盘IO,可以开启配置no-appendfsync-on-rewrite yes,表示在AOF重写期间不做fsync操作,默认为no。但是如此一来某些情况下会丢掉重写期间的所有数据。慎重啊,铁子
重启加载
讲RDB时提到,如果开启AOF优先加载AOF文件,否则执行RDB文件
AOF追加阻塞
当开启AOF持久化时,默认以及常用的同步硬盘策略是everysec(每s一刷),对于这种方式,Redis使用另一个线程每s执行fsync同步磁盘。试想一个问题,假设硬盘资源繁忙,fsync刷盘缓慢,主线程该如何做?
主线程写入AOF缓冲区后会对比上次AOF同步时间
如果距上次同步成功时间在2S内,主线程直接返回
如果距上次同步成功时间超过2s,主线程阻塞,直到同步操作完成
发现两个问题:
everysec配置最多可能丢失2s数据,不是1s
如果系统fsync慢会阻塞主线程
RDB-AOF混合持久化(redis 4.0+提供)
细细想来aofrewrite时也是先写一份全量数据到新AOF文件中再追加增量只不过全量数据是以redis命令的格式写入。
那么是否可以先以RDB格式写入全量数据再追加增量日志呢这样既可以提高aofrewrite和恢复速度也可以减少文件大小还可以保证数据的完毕性整合RDB和AOF的优点那么现在4.0实现了这一特性——RDB-AOF混合持久化。
【7】RDB-AOF混合持久化
(7.0)如果同时存在 RDB 与 AOF 故障恢复时会怎样?
参考:https://blog.csdn.net/weixin_43262188/article/details/90446158
思考:假设在有dump.rdb 文件的情况下,但是 AOF文件损坏,redis还能启动吗?
结论:
(1)如果开启了 aof 参数,会先以 aof 作为恢复、启动途径,关闭了aof 参数,才会用 rdb 快照来恢复、启动
(2)可以使用 redis 自带的 redis-check-dbp / redis-check-aof 来修复有问题、损坏的 rdb、aof 文件
(7.1)混合情况
综上所述RDB-AOF混合持久化体现在aofrewrite时,即在AOF重写时把frok的那个镜像写成RDB,后续AOF重写缓冲里的数据继续以命令的方式追加到AOF文件中。
要实现上面那行文字描述的效果,核心配置参数为 aof-use-rdb-preamble yes #4.0默认关闭,5.0及以后默认打开
写在最后:
小结:
redis提供两种持久化方式:RDB和aof
RDB使用一次性生成内存快照,产生的文件为二进制序列,非常紧凑,因此加载更快。但是由于其为快照,所以不能做到实时持久化,一般用于冷备和复制传输
save命令会阻塞主线程不建议使用,bgsave通过fork创建子进程生成RDB避免阻塞
AOF通过追加写命令到文件实现持久化,因为需要不断追加写命令,所以AOF需要定期执行重写来降低文件体积
如果写命令直接写入磁盘势必会造成性能过低,所以redis提供了一个AOF缓冲区。写命令写入到AOF缓冲区后续在调用fsync异步刷盘
AOF子进程执行期间使用copy-on-write机制和父进程共享内存,但是该技术只能看到fork那一瞬间内存的快照,所以需要一个AOF重写缓冲区,保存新的写入命令避免数据丢失
持久化阻塞主线程的场景有:fork阻塞和AOF追加阻塞,fork阻塞时间跟内存和系统有关,AOF追加阻塞说明磁盘资源紧张
(7.2)for 与 aof 阻塞问题
fork阻塞:在RDB和AOF重写时,redis主线程会开启一个fork子进程,由子进程完成持久化操作,可以通过info stats获取到latest_fork_usec指标,如果超过1秒,则需要作出优化调整。
AOF刷盘阻塞:开启AOF后,后台线程每秒对AOF文件做同步,如果同步超过2秒,redis会阻塞等待。也可以查看info persistence统计中的aof_delayed_fsync指标。
fork问题
一般是内存配置和使用问题
fork 应该是每GB数据20毫秒左右的消耗,具体可以在 Info stats 统计中查看 latest_fork_usec 指标获取最近一次fork操作的耗时,单位毫秒。
如何改善:
(1)使用物理机或高效的支持redis的虚拟化技术,避免使用xmen等低效虚拟化技术
(2)控制redis 单实例的最大可用内存,fork耗时和内存数据量大小成正比
(3)合理分配内存,要比较大,避免内存不足 fork 执行失败
(4)控制好fork的频率,比如 rdp 的频率,aof 的同步频率,避免不必要的全量复制。
Aof 追加阻塞、刷盘阻塞
一般是磁盘IO负载问题
通过 info persistence 统计,指标 aof_delayed_fsync 指标累加
【8】文章涉及到的命令及配置
命令 :手动备份RDB:save(阻塞) 和 bgsave(fork子进程)
配置: save m n 表示m秒内数据集存在n次修改时(可以配置多组条件,其中一个达标就触发),自动触发bgsave
命令 :debug reload 重新加载redis
日志文件名配置 : 文件保存在dir配置的指定目录下,文件名通过dbfilename配置指定。可以通过执行 config set dir{new Dir} 和 config set dbfilename{newFileName} 运行期动态执行。
开启aof: appendonly yes
appendfsync always|everysec|no : AOF缓冲区同步磁盘,always:总是同步,everysec:1s刷新,no:交给系统(大概30s)
命令 : bgrewriteaof :手动重写AOF
配置:auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
配置:auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的百分比(如auto-aof-rewrite-percentage 100 当为两倍大小是重写)。
配置:aof-rewrite-incremental-fsync:AOF每次重写一次刷盘大小,默认为32MB
配置:no-appendfsync-on-rewrite :AOF重写期间是否刷新AOF缓冲区,默认为no(刷新)
【参考文档】
【4】转自:CSDN:https://blog.csdn.net/ctwctw/article/details/105147277
【5】转自CSDN,原文链接:https://blog.csdn.net/qq_31387317/article/details/95315166