redis(二) AOF和RDB

146 阅读3分钟

AOF

Redis 防止数据丢失,采用了日志AOF和快照 RDB。与mysql中的binglog相似但又不同。

Mysql 中的binglog采用wal,在数据写入之前,记录日志,AOF是在数据写入之后,再写入日志。

AOF的日志内容

AOF中记录的是命令,类似于binglog中的stamengt模式。 例如 set key value 的命令,记录在AOF中如下

*3  // 代表有几个部分
$3	// 代表第一个部分有几个字节
set
$3  // 代表第二个部分有几个字节
key
$5  // 代表第三个部分有几个字节
value

AOF写入

AOF在命令执行后写入磁盘,此时如果正常写入,会阻塞redis的响应速度,在写入磁盘后再返回给客户端

Reids为了避免阻塞,提供了3中写入的方式,

  • Always 同步写回,会阻塞主线程
  • Everysec 每秒写回,每个写命令执行完,只是把日志放入AOF的缓存区,然后每隔一秒,把缓存中的内容,写入磁盘
  • No 操作系统控制回写,redis只负责把AOF放入缓存区,然后操作系统决定何时去回写

上面的3种方法,无法彻底的解决阻塞和AOF丢失的问题,择中的选择是Everysec,性能适中,数据丢失少。

AOF重写

假设 对同一个key,我们进行了100次的修改,那相比于本身的数据来看,AOF的大小是它的100倍,同时,当我们使用这个文件去恢复数据时,也会增加不必要的时间。因此,Redis提供了AOF 的重写功能。

用命令记录这个时刻下,整个redis中的数据。这样,在理想的情况下,AOF文件大小和数据是一比一的关系。在这个过程中,需要解决2个问题。

1、Reids的最新数据也需要写入,不然AOF就不够准确
2、不阻塞当前Redis正常的使用

当AOF重写时,主线程会fork一个子进程,fork 会把主线程的页表拷贝一份给 bgrewriteaof 子进程。也就是虚拟的映射关系拷贝一份,子进程也就可以访问主线程中的数据了。通过这一份数据,写入一个新的AOF日志中。

当此时发生,新数据的写入怎么办,Redis会准备一个AOF重写缓存,写入。最后再bgrewriteaof拷贝结束后,把重写缓存写入AOF,然后释放旧的AOF,采用新的AOF写入。

至于在重写时,为何需要采用2个AOF文件,主要是为了减少资源的竞争问题。防止主线程和bgrewriteaof 子进程竞争。

RDB快照

内存快照,redis提供2个命令来生成

  • save,在主线程中进行,生成快照时,无法响应正常命令
  • bgsave 创建一个子进程,专门写入RDB文件,避免了主线程的阻塞,redis默认的配置是这个

与AOF重写不同,RDB在生成是不会copy页表,而是直接读取内存中的数据,然后写入RDB。
更加不同的地方在于,AOF重写需要保证新的命令也可以写入,但是RDB需要保证,新的数据变更,不写入快照中。防止数据的抖动,采用了Copy-On-Write,当对一个key需要进行写入时,先copy一份副本,再写入。当bgsave子进程写到这个key时,采用副本中的数据写入。

RDB的回复速度比AOF更快,而且RDB使用2进制的方式保存文件,在读取和传输速度上更快。这2点决定了它,在redis集群的主从复制是,使用RDB复制

当每隔T时间去,生成RDB快照时,采用RDB和AOF的混合使用,第一次时,生成RDB文件,然后再T时间内,记录AOF文件,通过把上一次的RDB文件和AOF文件结合,可以得到下一个RDB。减少了RDB全量拷贝的时间和过程。

注意

在前面中提到过,redis会进行reHash,在AOF重写和生成RDB时,会停止