redis-2
redis的持久化
Redis 提供了 RDB 和 AOF 两种持久化方式。
RDB
RDB 是把内存中的数据集以快照形式写入文件,采用二进制压缩存储。
redis有两个命令用于生成RDB文件:
- SAVE
- BG SAVE
SAVE命令会阻塞redis服务进程,直到RDB文件创建完成为止。
BG SAVE和SAVE不同,BG SAVE会fork出一个子进程,然后子进程负责创建RDB文件,父进程就不受阻塞的继续处理命令请求。
BGSAVE的处理核心是利用系统的缺页异常.
linux内存管理--缺页异常处理
缺页异常在linux内核处理中占有非常重要的位置,很多linux特性,如写时复制,页框延迟分配,内存回收中的磁盘和内存交换,都需要借助缺页异常来进行,缺页异常处理程序主要处理以下四种情形:
- 请求调页: 当进程调用malloc()之类的函数调用时,并未实际上分配物理内存,而是仅仅分配了一段线性地址空间,在实际访问该页框时才实际去分配物理页框,这样可以节省物理内存的开销,还有一种情况是在内存回收时,该物理页面的内容被写到了磁盘上,被系统回收了,这时候需要再分配页框,并且读取其保存的内容。
- 写时复制:当fork()一个进程时,子进程并未完整的复制父进程的地址空间,而是共享相关的资源,父进程的页表被设为只读的,当子进程进行写操作时,会触发缺页异常,从而为子进程分配页框。
- 地址范围外的错误:内核访问无效地址,用户态进程访问无效地址等。
- 内核访问非连续性地址:用于内核的高端内存映射,高端内存映射仅仅修改了主内核页表的内容,当进程访问内核态时需要将该部分的页表内容复制到自己的进程页表里面。
缺页异常处理程序有可能发生在用户态或者内核态的代码中,在这两种形态下,有可能访问的是内核空间或者用户态空间的内存地址,因此,按照排列组合,需要考虑下列的四种情形,如图所示:
- 缺页异常发生在内核态
- 缺页异常发生在用户态
RDB中的BGSAVE正是利用缺页异常处理写时复制的过程,从而让子进程复制父进程的内存
linux写时复制 COW
引申至Java的copyOnWriteList
COW缺点?1. 缺页异常,还可能导致大量页置换 2. 需要预留内存给COW
如果刚好是读多写少的场景,就非常适用COW
AOF
AOF是将逐条redis命令保存下来。所以一般情况下,AOF的数据集是远大于RDB的。但是,AOF的数据往往比RDB更准确,因为AOF刷盘比RDB及时。
为什么使用了AOF还会丢失数据?
在AOF刷盘期间,fsync是无法保证缓存中的数据一定能持久化到硬盘上。
AOF有三种刷盘策略:
- everysec 每秒刷盘
- always 每次刷盘
- no 由操作系统决定
fsync问题可引申至其他也需要持久化的知识:mysql的binlog、redolog
everysec刷盘缺点
如果每秒需要刷盘的数据太多,导致一次刷盘不能全部刷盘完成,这时会阻塞硬盘。Redis每次刷盘前会检查距离上一次刷盘成功的时间,如果超过两秒,说明上一次刷盘尚未完成,此时Redis会等待上一次刷盘完成。而在等待上次刷盘完成的过程中,redis服务是处于阻塞状态,其他请求都会被阻塞。
所以使用everysec刷盘可能会导致丢失两秒数据。
AOF重写
AOF记录逐条指令,导致AOF文件非常大,使用AOF还原redis数据库时间增加。所以AOF提供重写机制,合并AOF记录里的指令,解决AOF文件体积膨胀问题。
AOF后台重写
与RDB中的BG SAVE类似,aof_rewrite函数会阻塞redis服务,所以redis将AOF重写放到子进程中处理。
使用子进程处理重写,可能会带来问题:当主进程有新数据写入时,会导致当前数据库数据和重写后的AOF文件记录不一致。
为了解决这种数据不一致的问题,redis增加了AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区。
当子进程在重写AOF时,主进程主要执行以下3个操作:
- 执行命令请求
- 将命令追加到现有的AOF文件中
- 将命令追加到AOF重写缓存中
当子进程完成重写AOF后,会向父进程发送一个完成信号,父进程会进行:
对新的AOF改名,原子性的覆盖旧AOF文件,完成新旧AOF文件的替换。
AOF重写缓冲可类比G1 SATB的缓冲,存储标记快照
网友评论