Redis持久化(AOF|RDB)
Redis持久化(AOF|RDB)
AOF
AOF是什么?
AOF全称是append only file, 即只追加文件。
AOF持久化是通过保存Redis服务器执行的写命令来记录数据库状态的。
AOF默认是不开启的,,需要在redis.conf文件中配置:
1 | appendonly yes |
AOF持久化配置项说明:
- appendonly:是否开启AOF持久化
- appendfilename:持久化文件名
redis处理写命令的时,会采取先写内存数据,再记录AOF日志的方式,这样做有两个好处:
- 减少额外检查开销: 只有内存数据被修改成功后才会写AOF日志,保证了日志都是可执行且正确的
- 不会阻塞当前的写操作
风险
如果命令执行完成,写日志之前宕机了,会丢失数据。
可能会阻塞下一条写命令的执行: 写操作和记录日志都是主进程(主进程中的redis_aof_write_thread线程),如果在刷盘时,服务器I/O压力过大,导致写硬盘速度很慢,就会阻塞后续的写命令
写AOF日志的过程
redis在执行完一个写命令之后,会以协议格式将被执行的写命令追加到日志缓存server.aof_buf中,接下来通过系统调用write(),将日志缓存server.aof_buf中的内容写入到AOF文件中。
有三种写回策略,需要通过设置redis.conf文件中appendfsync参数来配置:
AOF持久化配置项说明:
- appendfsync:持久化策略
- always:每个写命令执行完,立马同步地将日志写回磁盘,数据安全性最高,但性能最差
- everysec:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,
redis的后台异步线程
每隔一秒把缓冲区中的内容写入磁盘 - no:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由
操作系统
决定何时将缓冲区内容写回磁盘
AOF重写
AOF重写是指对AOF文件进行压缩,只保留可以恢复数据的最小命令集合,可以有效减小AOF文件的体积,以提升数据恢复的性能。
重写触发的条件:
auto-aof-rewrite-min-size
:表示运行AOF重写时文件的最小大小,默认为64MB。auto-aof-rewrite-percentage
:这个值的计算方式是,当前aof文件大小和上一次重写后aof文件大小的差值,再除以上一次重写后aof文件大小。也就是当前aof文件比上一次重写后aof文件的增量大小,和上一次重写后aof文件大小的比值。
流程:读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的 AOF 文件
,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件
为什么要新建一个AOF文件?
如果在原先的AOF文件进行重写,如果重写失败,就会出现数据不一致,导致污染,可能无法用于恢复
为什么是后台子进程而不用子线程?
子线程会共享内存,主进程要写,子线程要读,要加锁防止读写并发操作,性能差
采用子进程,写时复制提高性能:
采用子进程,主进程会为子进程分配与主进程相同的页表,即虚拟空间映射到相同的物理空间,达到数据共享,并且不需要加锁操作.但会对这块共享的物理内存设置为只读状态,如果主进程 或 子进程 对这块内存发生写操作,会触发写时复制.CPU 触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这样就避免了加锁操作,通过写时复制,来提高性能.(简单来说就是如果主进程在子进程进行AOF重写时,没有执行写入操作,就不会真正分配内存资源,就是写时复制技术COW的应用)
AOF后台重写机制描述
进行AOF日志后台重写时,主进程fork子进程,并且为子进程分配与主进程相同的页表,即虚拟空间映射到相同的物理空间,达到数据共享,并且通过写时复制,来避免加锁
子进程收集内存中的键值对转化为一条命令,写入新的AOF文件,但在写入过程,主进程仍然可以处理命令,当碰到写操作命令时,会发生以下操作:
触发写时复制过程,CPU 触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后主进程才会对内存进行写操作
但子进程收集的AOF文件 数据就不一致了,如何解决?
redis 在AOF后台重写触发时,会分配一个AOF重写缓冲区,重写过程,主进程执行更新内存数据时,需要执行以下几件事情:
- 执行命令,更新内存数据
- 将命令记录到两个位置:旧的AOF文件和AOF重写缓冲区
- 当子进程 AOF重写完毕,会发送一个信号告诉主进程,主进程调用信号处理函数会做以下事情:将AOF重写缓冲区的数据直接追加到新的 AOF文件末尾,替换旧的AOF文件,重写过程就结束。信号处理函数执行完毕,主进程就可以继续处理命令了
后台重写,什么阶段会阻塞主进程?
- 创建子进程的途中,由于要复制父进程的页表等数据结构: 阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长
- 创建完子进程后,如果父进程修改了共享数据(子进程一般只读),就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长
- 子进程 AOF重写完毕,发送信号通知主进程,主进程会调用信号处理函数,执行时也会对主进程造成阻塞
什么优化,让主进程阻塞时间尽量缩小?
写时复制,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致主进程长时间阻塞的问题。当冲突发生,再进行解决,来提高效率。也可以减少内存分配。
RDB
RDB快照是什么?如何生成?
RDB 快照记录的是内存中的实际数据,二进制格式;所以在redis重启恢复数据时,只需要加载rdb文件到内存即可,速度较快
redis提供了两个命令来主动生成rdb文件
●save:主进程来执行全量快照操作,若内存数据太多,会阻塞主进程,不推荐
●bgsave:主进程fork 子进程,来执行全量快照操作,这种思想在AOF后台重写日志也用过,避免主线程的阻塞
还可以通过配置参数,来生成快照
默认配置:执行的是 bgsave 命令,也就是会创建子进程来生成 RDB 快照文件。1
2
3save 900 1
save 300 10
save 60 10000
只要满足上面条件的任意一个,就会执行 bgsave,它们的意思分别是:
900 秒之内,对数据库进行了至少 1 次修改;
300 秒之内,对数据库进行了至少 10 次修改;
60 秒之内,对数据库进行了至少 10000 次修改。
执行RDB快照过程是怎样的?执行快照时,数据能被修改吗?
Redis 同样采用写时复制
技术,来支持快照,执行快照时数据能被修改,具体过程:
主进程fork创建一个子进程,来执行bgsave命令,主进程会将自己的页表复制一份给子进程,虚拟空间指向相同的物理空间,来达到空间共享.子进程在记录实际数据到RDB文件过程中,若主进程没有执行写操作命令,那么共享空间就不会发生冲突,也不会发生写时复制。
若主进程需要执行写操作命令,如主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,这块数据的物理内存就会被复制一份(键值对 A’),然后主线程在这个数据副本(键值对 A’)进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。
bgsave 快照过程中,如果主线程修改了共享数据,发生了写时复制后,本次RDB 快照保存的是原本的内存数据,而主线程刚修改的数据,是没办法在这一时间写入 RDB 文件的,只能交由下一次的 bgsave 快照
怎么将RDB文件加载到内存?
RDB 文件的加载工作是在redis服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。
RDB 快照的缺点
每一次执行RDB快照,记录的是内存中的所有数据,相当于一个全量快照,当数据较多时,频繁记录快照,虽然可以采用子进程来执行,但还是会影响redis的性能,如果记录的太不频繁,那么当服务器宕机,丢失的数据就非常多
写时复制有什么极端场景?
在 Redis 执行 RDB 持久化期间,刚 fork 时,主进程和子进程共享同一物理内存,但是途中主进程处理了写操作,修改了共享内存,于是当前被修改的数据的物理内存就会被复制一份。那么极端情况下,如果所有的共享内存都被修改,则此时的内存占用是原先的 2 倍。所以,针对写操作多的场景,我们要留意下快照过程中内存的变化,防止内存被占满了。
AOF日志 & RDB快照 区别
它们都是redis数据库内存持久化的技术
- 记录的内容不同:AOF 文件的内容是操作命令;RDB 文件的内容是二进制数据。
- 恢复数据时,耗费的时间不同:RDB 恢复数据的效率会比 AOF 高些,因为直接将 RDB 文件读入内存就可以,AOF 需要额外执行操作命令的步骤才能恢复数据,速度慢
- 在服务器发生故障时,丢失数据多少方面:RDB是一个全量快照,不能频繁执行,否则会影响性能,所以丢失数据相对来说较多.AOF支持秒级记录命令到AOF文件中,所以丢失的数据相对来说较少
AOF|RDB混合持久化
AOF|RDB进行混合持久化有什么优点?
混合持久化可以利用这两种日志的优点:
AOF可以支持秒级记录日志,使得数据更少的丢失,RDB在数据恢复时非常快,不需要重新执行命令
混合持久化的过程只发生在AOF日志重写的过程
,如下:
- 当AOF日志达到阈值时,触发后台重写机制,主进程fork创建子进程,并且将主进程的页表复制一份给子进程
- 子进程与主进程共享空间,对当前内存数据执行RDB快照,将RDB格式的日志记录到新的AOF文件中
- 并且在重写过程,主线程处理的写操作命令会被记录在AOF重写缓冲区里,当RDB快照记录完毕,AOF重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件
- 写入完成后通知主进程,将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。
好处:
重启Redis加载数据的时候,由于前半部分是RDB内容,这样加载的时候速度会很快。加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。