系列文章目录
Redis之持久化机制篇(本文)
文章目录
前言
最近复习Redis发现了之前学的好多东西都忘了,写篇文章来加深印象。本文主要是对Redis的重写机制进行理解和复习,如读者认为有错误的地方请指出,定修正。主要参考小林coding的文章。
一、Redis为啥要有持久化机制?
众所周知,Redis数据是存储在内存的,如果Redis缓存服务器宕机了,比方说断电,那数据直接就没了。这样就会导致大量请求直接给到数据库服务器,数据库可能会undinable。所以要把Redis存在内存的数据持久化在磁盘,类似打游戏的存档机制。
二、Redis有几种持久化机制?有什么区别?
Redis有RDB快照和AOF日志两种持久化机制。顾名思义,快照嘛就是咔嚓一下记录一瞬间所有在内存的数据,记住这个照片就是RDB文件(写入磁盘的),至于什么时候按下这个“快门键”就看你怎么配置了。而日志呢就是就是每条写操作(增删改)都给你记录下来,像记流水帐一样,当然它不会记录读操作(因为记录下来也没啥意义呀)。但是,要注意的是,这个记录并不是直接写道磁盘,而是写到一个缓冲区(在内存中),至于什么时候将这个缓冲区的数据写入磁盘,就要看我们怎么配置了。
三、RDB和AOF持久化的配置命令
前文说到,要什么时候按下“快门键”和什么时候把数据写入磁盘都要我们配置,它们的配置都在 redis.conf 配置文件中。
RDB文件配置命令
RDB有两种配置命令,save 和 bgsave,区别在于生成RDB文件时是在主线程执行:
save命令:在主线程生成RDB文件。如果写入RDB文件时间过长,会阻塞主线程。
bgsave命令:创建一个子进程 (注意:这里是进程不是线程哈)来生成RDB文件。无论写入RDB文件时间长短,不会使主线程阻塞。
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
AOF文件配置命令
默认是使用RDB文件方式,但是AOF实时性更好。通过 appendonly 参数开启:
appendonly yes
开启后每条写命令(增、删、改)都会写入缓冲区 server.aof_buf 中。要将缓冲区的数据写入磁盘的AOF文件则根据 appendfsync 配置,有三种方式:always、everysec 和 no
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显式地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
四、持久化时又有数据变更?
我们可能会遇到这种情况,在持久化过程时,又有数据变更,那我就有疑问了,这些变更的数据会被持久化嘛?为了解开这个疑惑,先来搞懂它们是怎么进行持久化的。
RDB快照持久化过程
前文提到,RDB快照有两种持久化方式,save 和 bgsave 两种。
1.RDB快照持久化之save
前文已知,save是直接在主线程进行持久化的,但是redis是单线程的,所以整个Redis服务器会阻塞。因此,使用save命令期间是不能进行数据修改的!性能极其拉跨😒
2.RDB快照持久化之bgsave
与save命令不同, bgsave命令不会阻塞主进程,而是通过 fork() 创建子进程 (注意:这里是进程不是线程哈) 来进行持久化,所以bgsave持久化时是可以进行数据变更的。这就涉及到 写时复制技术(Copy-On-Write, COW)
先来说明一下 fork() 创建子进程的过程(借用小林哥的一张图😄):
如果是线程的话就不需要借助页表了,因为是 fork() 出子进程,所以需要借助页表来实现共享内存。即然主进程和子进程共享内存,如果子进程在持久化时,主进程修改了数据,那子进程不也一起变了吗?是的哈,所以要使用 写时复制技术 ,就是在内存复制一块一模一样的数据块出来,就是拷贝个副本,然后各搞各的。 (学过操作系统会好理解些)
再借小林哥的一张图理解一下 写时复制技术 :
有人可能会不理解,为啥不将刚修改的数据也一起写入呢?你看RDB叫啥,是不是快照,都已经照了你咋还能改呢doge。(这是我瞎扯的,只是为了方便记忆)
![图片替换文本](https://i-blog.csdnimg.cn/blog_migrate/8341c602982abb696598c2d6af030770.png)
AOF日志持久化过程
AOF日志持久化过程有三种配置方式,不过它们的区别只在于持久化时间,它们持久化的过程是一致的。那我们就来扒开AOF持久化过程的裤衩(还没到把底裤的能力😂)。还是借用小林哥的一张图说明一下AOF日志持久化过程。首先,主进程会执行写操作命令(增删改),再把该条操作记录到AOF缓冲区,最后再持久化到磁盘中(这一部后面细说)。
这里有几点要注意一下就是:
①执行命令操作成功后再执行写日志,都在主进程执行可以避免额外的检查开销,不会阻塞当前写操作命令的执行,但写日志可能会阻塞下一个命令的执行;
②写日志是先写到AOF缓存,可能会丢失数据;
③把AOF缓存的数据写到磁盘是我们自己可以设置的就是三种写回策略;
④缓冲区有两个,一个是在用户态的AOF缓冲区,一个是内核态的page_cache缓冲区(后面细说);
⑤写日志≠写回,写日志是写到内存的,写回是由内核缓冲区持久化到磁盘的(后面细说);
Tips:有人可能会疑问,为啥在把日志写入内存时不需要像写入磁盘时一样有几种写入方式,很简单,因为内存它太快了。
三种写回策略
在 redis.conf 文件中,可以设置 appendfsync 为 Always/Everysec/No:
- Always:表示每挑写命令执行成功就把AOF日志写回磁盘;
- Everysec:表示每次写命令执行成功后,先将命令写入AOF文件的内核缓冲区,然后每隔一秒将缓冲区数据写回磁盘;
- No:表示把写回时机交给操作系统,每次执行写命令成功,先将命令写入AOF文件的内核缓冲区,再有操作系统决定何时将内存写回磁盘。
Tips: 三种策略都无法平衡主进程阻塞和数据丢失问题
从上面分析已经可以得出结论,如果AOF在持久化时,是会阻塞主进程,所以是不会有数据更新的!
写回详解(细说环节)
虽然已经确定我们在持久化的过程中不能有新数据加入,但我们还是要来看一下写回的一些更底层的细节。看一下Redis写入AOF日志的全过程(小林哥的):
- 写操作命令执行成功后,将命令加到AOF缓冲区(用户态);
- 调用write() 系统调用函数,写道AOF文件的内核缓冲区page cache,迭代内核写入磁盘;
- 我们的写回策略决定了将数据有内核缓冲区到磁盘的时机,要么自己调用 fsync() 函数(将数据写入磁盘函数),要么让系统调用。
这里要注意的是,我前面提到的AOF缓冲区都是指用户态下的server.aof_buf,而内核还有一个缓冲区page cache。前面三种写回策略都是指从AOF文件的内核缓冲区到硬盘的,而不是AOF的缓冲区到硬盘!
五、重启Redis谁更快?
Redis重启的话会读取RDB文件或AOF文件,为了让重启速度更快,AOF日志模式有一种niubility的操作,那就是AOF重写机制。顾名思义,重写就是把AOF文件重新写一遍压缩文件大小,当然不是复制粘贴哈。先来各例子说明一下:假设我把 key=redis value=rdb 的记录改成了 key=redis value=aof ,那我们前面那一条记录还有意义吗?答案是没有意义的,因为我都改成了 aof 了,记着过去的记录也没啥意义,活在当下嘛(只看最新的记录)。
其实,AOF重写机制就是这种策略来达到压缩AOF文件的目的。此外,我们重写的时候不是直接在原AOF文件操作,因为如果 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,可能无法用于恢复使用。(我们实际生活写word文档也是如此😂)
AOF重写机制
AOF重写机制是在子进程 bgrewriteaof 进行的,所以这里和前面很类似的会有fork操作,父进程就是执行命令或写AOF日志的,而子进程进行AOF文件重写。在复制父进程的页表等数据结构时会阻塞父进程,在父进程或子进程修改了数据发生写时复制时也会阻塞父进程。注意这里只会复制主进程修改的物理内存数据,没修改物理内存还是与子进程共享的。
有个问题就是,如果我在重写时期修改了数据,那就会导致子进程和主进程数据不一致。解决方案就是AOF重写缓冲区(和AOF缓冲区不一样哈),这个缓冲区在创建bgrewriteaof 子进程之后开始使用,在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 「AOF 缓冲区」和 「AOF 重写缓冲区」。
AOF重写工作:我最开始误以为重写时依据原AOF文件来的,但其实不是的。AOF重写的工作是 先扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志。
AOF重写完成后会给主进程发异步信号,主进程接收到后,调用一个信号处理函数,下面就是这个函数的工作:
- 将 AOF 重写缓冲区中的所有内容追加到新的 AOF 的文件中,使得新旧两个 AOF 文件所保存的数据库状态一致;
- 新的 AOF 的文件进行改名,覆盖现有的 AOF 文件。
总结
本文主要讲解了RDB快照和AOF日志的持久化方式和一些持久化技术。比方说AOF日志的重写机制等。其实还有一种是RDB混合AOF持久化技术,后面有时间回来再补充!
疑问
下面是我作为一个菜鸟的疑问,有大佬懂还烦请指教指教!(膜拜大佬.jpg)
- Redis是单线程,fork() 子进程后是怎么运行的(挖坑:有时间写一下Redis的IO多路复用博客)