Redis持久化【AOF】

84 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

Redis如何实现数据不丢失?

redis的读写操作都是作用在内存中,我们都知道内存中的数据是暂时的,在断电等突发状况时会丢失数据。并且redis退出也会刷新数据。为了防止数据丢失,redis实现了数据持久化的机制,将数据存储到磁盘上。

Redis有哪几种持久化机制?

  • AOF日志--每执行一条写命令,就将命令追加到一个文件中
  • RDB快照--将某一个时刻到内存的数据,以二进制的方式写入磁盘
  • 混合持久化方式--集成了AOF和RDB的优点

AOF日志是如何实现的?

redis在执行完一条写操作后,以追加的方式写入到文件中,在redis重启时,会读取该文件记录的命令,然后逐一执行命令的方式来恢复数据。

redis执行写命令后,记录在AOF日志里的内容是怎样的呢?

*3 表示3个部分

$3 表示3个字节

通过redis.conf里的appendonly 开启,默认是rdb

为什么先执行写命令,再把数据写入日志呢?

先执行写命令,再记录到日志中的好处是:

  • 避免额外的检查开销,假如先记录到日志中,如果写操作有错误,将会在redis重启恢复时出错。
  • 不会阻塞当前写操作的执行

但是也存在风险:

  • 数据可能会丢失,写操作和记录日志是两个过程,当写入硬盘的过程中发生宕机,就会存在丢失数据的可能性。
  • 可能阻塞其他操作,不会阻塞之前的写命令,但是由于记录日志的操作还是在主线程中,会阻塞之后的操作。

AOF写回策略有哪几种?

我们首先得知道,写回是啥,发生在哪里?

下面是redis写入AOF日志的过程:

过程是这样的:

  1. redis执行完写命令之后,会将命令追加到server.aof_buf缓冲区,即aof缓冲区
  2. 然后通过write()系统调用,将aof_buf缓冲区的数据写入到AOF文件,此时数据并没有写入到磁盘,而是拷贝到了内核缓冲区page cache,等待内核将数据写入硬盘;
  3. 具体内核缓冲区的数据什么时候写入到磁盘,由内核决定。

redis写回就是内核缓冲区的数据写入到磁盘,由内核决定。

写回策略就是决定什么时候写入到磁盘。

在redis.conf文件中的 appendsync配置

  • Always -- 每次写命令执行完后,同步将AOF日志数据写回硬盘。
  • Everysec -- 每次写命令执行完后,先将命令写入到AOF文件到内核缓冲区,然后每隔一秒将缓冲区的内容写回到磁盘
  • No 意味着不由redis控制写回硬盘的时机,转交给操作系统控制,每次写操作完成后,将命令写入到AOF文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

各有优缺点:

AOF日志过大,会出发什么机制?

会触发重写机制。因为,AOF日志是记录redis写操作方便恢复数据的文件,当AOF日志文件过大,会导致redis重启恢复数据太慢。所以为了避免AOF日志文件过大,提供了AOF重写机制。

重写机制,就是当AOF文件大小超过阈值时,启动该机制,来压缩AOF文件。

AOF重写机制的过程是,在重写时,读取数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的AOF文件中,等到全部记录完后,用写的AOF文件替换现有的AOF文件。

相当于压缩了AOF文件。

详解重写AOF日志的过程是怎样的?

redis后台重写AOF的过程是由后台子进程bgrewriteaof来完成的。

为什么采用子进程的方式?

  • 子进程进行AOF重写期间,主进程可以继续处理命令,避免阻塞
  • 子进程带有主线程的数据副本,这里使用子进程而不是线程,因为如果是使用线程,多线程会共享内存,一个线程修改内存数据,就会加锁,降低性能。而使用子进程,父子进程共享内存,共享的内存是只读的,任意一方试图修改内存,就会先由于权限问题导致的缺页中断处理函数发生物理内存复制,即写时复制,复制完后才会进行修改。

触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的 AOF 文件)。

在重写过程中,主进程可以正常处理命令,如果主进程修改数据,发生写时复制后,主进程修改数据,导致父子进程的内存数据不一致怎么办?