作者:风过无痕啦啦 | 来源:互联网 | 2023-10-12 14:08
Redis持久化的研究Redis的持久化的方式----AOF在redis中存在一个日志文件(aof日志),此日志文件中保存着在redis中执行的有效的命令;当需要进行数据恢复时就会
Redis持久化的研究
Redis的持久化的方式----AOF
在redis中存在一个日志文件(aof日志),此日志文件中保存着在redis中执行的有效的命令;
当需要进行数据恢复时就会加载该文件,在redis重新执行一边来达到恢复数据的目的;
此处需要注意
AOF日志文件采用的是写后日志
写前日志:在实际写数据之前,先将修改数据的记录写道日志文件中;
写后日志:先执行命令,把数据写入到内存,然后再去记录日志
AOF采用写后日志的原因
在AOF文件中记录的并不是Redis识别的指令,而是经过了一次转换
//执行的命令
set test01 18
//在AOF文件中存储的"命令"
*3 //*3”表 ⽰当前命令有三个部分
$3 //此命令3个字节
set
$6 //此键6个字节
test01
$2 //此值2个字节
18 //*3”表 ⽰当前命令有三个部分,每部分都是由“$+数字”开头,后⾯紧跟着具体的命令、键或值。这⾥,“数 字”表⽰这部分中的命令、键或值⼀共有多少字节。
为了避免额外的检查开销,Redis在向AOF⾥⾯记录⽇志的时候,并不会先去对这些命令进⾏语法检查。所以,如果先记⽇志再执⾏命令的话,⽇志中就有可能记录了错误的命令,Redis在使⽤⽇志恢复数据 时,就可能会出错。
写后⽇志这种⽅式,就是先让系统执⾏命令,只有命令能执⾏成功,才会被记录到⽇志中,否则,系统就会直接向客⼾端报错。所以:
Redis使⽤写后⽇志这⼀⽅式的⼀⼤好处是,可以避免出现记录错误命令的情况。
第二个好处就是它是在命令执⾏后才记录⽇志,所以不会阻塞当前的写操作。
AOF的潜在风险
风险一:
如果刚执⾏完⼀个命令,还没有来得及记⽇志就宕机了,那么这个命令和相应的数据就有丢失的⻛ 险。如果此时Redis是⽤作缓存,还可以从后端数据库重新读⼊数据进⾏恢复,但是,如果Redis是直接⽤作数据库的话,此时,因为命令没有记⼊⽇志,所以就⽆法⽤⽇志进⾏恢复了。
风险二:
[重要知识点]:redis中的aof文件写入的操作是主线程执行的
如果在把⽇志⽂件写⼊磁盘时,磁盘写压⼒⼤,就会导致写盘很慢,进⽽导致后续的操 作也⽆法执⾏了。
(当大量的请求进来的,如果此时主线程在执行向AOF文件写数据的任务,那么后面进来的请求就会被阻塞,导致出现卡顿------查询操作也会进入等待)
三种回写的策略
经过分析不难发现:AOF持久化的两种风险都是因为落盘导致的
(落盘--->主线程向AOF文件中写数据 aof文件存在于磁盘 在进行磁盘读写都相较慢)
所有AOF配置项也提供了三个可选值------->三种回写的策略
Always:
同步写回:每个写命令执⾏完,⽴⻢同步地将⽇志写回磁盘。
优点:可以做到基本不丢数据
缺点:每⼀个写命令后都有⼀个慢速的落盘操作,不可避免地会影响主线程性能;
Every sec:
每秒写回:每个写命令执⾏完,只是先把⽇志写到AOF⽂件的内存缓冲区,每隔⼀秒把缓冲区中的内容写⼊磁盘。
优点:避免了“同步写回”的性能开销,减少了对系统性能的影响
缺点:如果发⽣宕机,上⼀秒内未落盘的命令操作仍然会丢失。
No:
操作系统控制的写回:每个写命令执⾏完,只是先把⽇志写到AOF⽂件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
“操作系统控制的写回”在写完缓冲区后,就可以继续执⾏后续的命令,但是落盘的时机已经不在 Redis⼿中了,只要AOF记录没有写回磁盘,⼀旦宕机对应的数据就丢失了;
优点:效率最高,主线程完全不受影响
缺点:随时都可能丢失数据
选择的方式:
可以根据系统对⾼性能和⾼可靠性的要求,来选择使⽤哪种写回策略了。
- 想要获得⾼性能,就选择No策略;
- 如果想要得到⾼可靠性保证,就选择Always策略;
- 如果允许数据有⼀点丢 失,⼜希望性能别受太⼤影响的话,那么就选择Everysec策略。
AOF文件过大问题
AOF是以⽂件的形式在记录接 收到的所有写命令。随着接收的写命令越来越多,AOF⽂件会越来越⼤。这也就意味着,我们⼀定要⼩⼼ AOF⽂件过⼤带来的性能问题。
⼀是,⽂件系统本⾝对⽂件⼤⼩有限制,⽆法保存过⼤的⽂件;
⼆是,如果⽂件太⼤,之后再往⾥⾯追加命令记录的话,效率也会变低;
三是,如果发⽣宕机,AOF中 记录的命令要⼀个个被重新执⾏,⽤于故障恢复,如果⽇志⽂件太⼤,整个恢复过程就会⾮常缓慢,这就会 影响到Redis的正常使⽤。
重写机制--针对AOF文件过大的方案
Redis根据数据库的现状创建⼀个新的AOF⽂件,也就是说,读取数据库中的所有键值对,然后对每⼀个键值对⽤⼀条命令记录它的写⼊
重写机制的几个重要点:
主线程会forkc出后台的bgrewriteao子进程,不是由主线程完成;
Redis会读取数据库中的所有键值对,然后对每⼀个键值对⽤⼀条命令记录它的写⼊。--(旧⽇志⽂件中的多条命令,在重写后的新⽇志中变成了⼀条命令。)
重写的过程总结:
一个拷贝:
“⼀个拷⻉”**就是指,每次执⾏重写时,主线程fork出后台的bgrewriteaof⼦进程。此时,fork会把主线程 的内存拷⻉⼀份给bgrewriteaof⼦进程,这⾥⾯就包含了数据库的最新数据。然后,bgrewriteaof⼦进程就 可以在不影响主线程的情况下,逐⼀把拷⻉的数据写成操作,记⼊重写⽇志。
两处日志:
因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,第⼀处⽇志就是指正在使⽤的AOF⽇志,Redis会把这个操作写到它的缓冲区。这样⼀来,即使宕机了,这个AOF⽇志的操作仍然是⻬全的,可 以⽤于恢复。【aof日志缓冲区刷盘很快,丢数据最低可以控制到1s,业务能容忍】
⽽第⼆处⽇志,就是指新的AOF重写⽇志。这个操作也会被写到重写⽇志的缓冲区。这样,重写⽇志也不会丢失最新的操作。等到拷⻉数据的所有操作记录重写完成后,重写⽇志记录的这些最新操作也会写⼊新的 AOF⽂件,以保证数据库最新状态的记录。此时,我们就可以⽤新的AOF⽂件替代旧⽂件了。
[等子线程完成了新的AOF日志的重写就会向主线程发信号,当主线程接收到了之后,
1.就会将缓存去的数据向新的日志中保存,
2.将新的日志文件替换掉原有的日志文件]
注意
若原有的内存数据是A, 那重写AOF时,fork后父子进程都会指向A地址,当新的写入进来时,父进程会拷贝数据(新的key直接申请内存就写了),而子进程访问A逐渐写入到重写AOF中,写完通知父进程,父进程会把写到缓冲的新key,再追加到重写AOF中。
这里只有AOF缓冲区,AOF重写缓冲区(这是两个不同缓冲区),写入新数据会写入两个缓冲区。之所以写入AOF缓冲区(后续写入AOF文件)是为了防止宕机而冗余的数据,而AOF重写缓冲区是在重写日志文件生成后,再追加到重写日志文件中,最后再将重写日志文件命名为AOF日志文件。
这两个配置项的意思是,在aof文件体量超过64mb,且比上次重写后的体量增加了50%时自动触发重写。
深入理解(常回顾理解)
AOF 日志重写的时候,是由 bgrewriteaof 子进程来完成的,不用主线程参与,我们今天说的非阻塞也是指子进程的执行不阻塞主线程。但是这个重写过程会有没有其他潜在的阻塞风险
a. fork子进程,fork这个瞬间一定是会阻塞主线程的(注意,fork时并不会一次性拷贝所有内存数据给子进程),fork采用操作系统提供的写实复制(Copy On Write)机制,就是为了避免一次性拷贝大量内存数据给子进程造成的长时间阻塞问题,但fork子进程需要拷贝进程必要的数据结构,其中有一项就是拷贝内存页表(虚拟内存和物理内存的映射索引表),这个拷贝过程会消耗大量CPU资源,拷贝完成之前整个进程是会阻塞的,阻塞时间取决于整个实例的内存大小,实例越大,内存页表越大,fork阻塞时间越久。拷贝内存页表完成后,子进程与父进程指向相同的内存地址空间,也就是说此时虽然产生了子进程,但是并没有申请与父进程相同的内存大小。那什么时候父子进程才会真正内存分离呢?“写实复制”顾名思义,就是在写发生时,才真正拷贝内存真正的数据。
b、 fork出的子进程指向与父进程相同的内存地址空间,此时子进程就可以执行AOF重写,把内存中的所有数据写入到AOF文件中。但是此时父进程依旧是会有流量写入的,如果父进程操作的是一个已经存在的key,那么这个时候父进程就会真正拷贝这个key对应的内存数据,申请新的内存空间,这样逐渐地,父子进程内存数据开始分离。
AOF重写不复用AOF本身的日志,一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。二是如果AOF重写过程中失败了,那么原本的AOF文件相当于被污染了,无法做恢复使用。所以Redis AOF重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的AOF文件产生影响。等重写完成之后,直接替换旧文件即可。
思考
1、如果写入内存成功了,在写入aof日志的时候失败了,宕机数据会不会丢失,有什么处理机制吗
我的理解:
当Redis只开启了aof的持久化时:没有将数据写入到日志文件就宕机,那么数据是肯定会丢失的;
所以采用RDB持久化配合AOF的持久化一起使用,数据的安全性将更加高
redis-check-aof --fix xx.aof
2、什么时候会触发AOF 重写呢?
①: 手动发送“bgrewriteaof”指令,通过子进程生成更小体积的aof,然后替换掉旧的、大体量的aof文件
②:配置自动触发
1)auto-aof-rewrite-percentage 50
2)auto-aof-rewrite-min-size 64mb
关于AOF的总结
AOF两大好处:
1、避免出现记录错误命令的情况;
2、它是在命令执行后才记录日志,所以不会阻塞当前的写操作。;
AOF两大风险:
1、还没有来得及记日志就宕机,命令和相应数据就丢失了;
2、日志写入磁盘,影响后续AOF落盘操作;
AOF的工作原理:
1、Redis 执行 fork() ,现在同时拥有父进程和子进程。
2、子进程开始将新 AOF 文件的内容写入到临时文件。
3、对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
4、当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将AOF重写缓存中的内容全部写入到新的AOF文件中。
5、对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。
拓展了解
写时复制机制(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。