侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Redis的持久化机制与写时复制

再见理想
2022-05-27 / 0 评论 / 0 点赞 / 580 阅读 / 1,874 字

一,简介

RDB做镜像全量持久化,AOF做增量持久化。因为RDB会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要AOF来配合使用。在redis实例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期的操作指令来实现完整恢复重启之前的状态。


二,RDB

指定的时间间隔内将内存的数据集快照写入磁盘。

当 Redis 需要做持久化时,Redis 会 fork 一个子进程,子进程将数据写到磁盘上一个临时 RDB 文件中。当子进程完成写临时文件后,将原来的 RDB 替换掉,这样的好处就是可以 写时复制 copy-on-write

如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久。

文件保存在 dump.rdb 文件中。
默认保存时间:

900sec 1key :15分钟内改了一次
300sec 10key
60sec 10000key


2.1,写时复制 copy-on-write(COW)

核心思路:在fork出子进程后,与父进程共享内存空间,两者只是虚拟空间不同,但是其对应的物理空间是同一个; 只有在父进程发生写操作修改内存数据时,才会真正去分配内存空间,并复制内存数据,而且也只是复制被修改的内存页中的数据,并不是全部内存数据;


Linux 中 CopyOnWrite 实现原理

fork() 之后,kernel 把父进程中所有的内存页的权限都设为 read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。
当其中某个进程写内存时,CPU硬件检测到内存页是 read-only 的,于是触发页异常中断 page-fault,陷入 kernel 的一个中断例程。中断例程中,kernel 就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

CopyOnWrite 减少分配和复制资源时带来的瞬时延迟,同时减少不必要的资源分配;但如果父子进程都需要进行大量的写操作,会产生大量的分页错误(页异常中断page-fault);


Redis 中的 CopyOnWrite

在读《Redis设计与实现》关于哈希表扩容的时候,发现这么一段话:

执行 BGSAVE 命令或者 BGREWRITEAOF 命令的过程中,Redis 需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子进程存在期间,服务器会提高负载因子的阈值,从而避免在子进程存在期间进行哈希表扩展操作,避免不必要的内存写入操作,最大限度地节约内存。

Redis在持久化时,如果是采用 BGSAVE 命令或者 BGREWRITEAOF 的方式,那 Redis 会 fork 出一个子进程来读取数据,从而写到磁盘中。

总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。

而在 rehash 阶段上,写操作是无法避免的。所以 Redis 在 fork 出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。


三,ROF

以日志形式来记录每个写操作,Redis重启就根据日志文件顺序执行以完成数据恢复。

1,文件保存在 appendonly.aof

2,Aof的配置策略

(默认不开启-> appendonly no)

Appendfsync:
① Always -> 同步操作,每次发生数据改变立即记录到磁盘;
② Everysec -> 默认,异步操作,每秒记录;
③ No


3,Aof Rewirte

定义:aof 采用文件追加方式,文件会越来越大,为避免这种情况,新增了重写机制,当AOF文件大小超过设定的阈值时,redis就会启动aof文件的内容压缩,只保留可以恢复数据的最小指令集。可使用命令 -> bgrewriteaof。

原理:fork一条新进程处理。

触发机制:Redis 会记录上次重写时 aof 大小,默认配置是当文件大小是上次 rewrite 后的大小的一倍且大于64M时触发。

优缺点:灵活设置,每秒同步/每操作同步,好处是最多也只会丢失不超过2秒数据
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。


4,fsync

AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的。

Linux 的 glibc 提供了 fsync(int fd) 函数可以将指定文件的内容强制从内核缓存刷到磁盘。只要 Redis 进程实时调用 fsync 函数就可以保证 aof 日志不丢失。但是 fsync 是一个磁盘 IO 操作,它很慢!如果 Redis 执行一条指令就要 fsync 一次,那么 Redis 高性能的地位就不保了。

所以在生产环境的服务器中,Redis 通常是每隔 1s 左右执行一次 fsync 操作,周期 1s 是可以配置的。这是在数据安全性和性能之间做了一个折中,在保持高性能的同时,尽可能使得数据少丢失。


四,Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

0

评论区