Redis持久化

Redis 的读写操作都是在内存中,所以 Redis 的性能高,但是当 Redis 重启或宕机后,内存中的数据就会丢失,为了保证内存中的数据不会丢失,故Redis 采用了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启后就能够从磁盘中恢复原有的数据。

Redis持久化的三种方式:

  • AOF日志:Redis每执行一次操作命令,就把该命令以追加的方式写入磁盘相应的文件中
  • RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘
  • 混合持久化方式:Redis 4.0新增的持久化方式,集成了AOF与RDB的优点

AOF日志实现

Redis每执行一次操作命令,就把该命令以追加的方式写入磁盘相应的文件中,在Redis重启后,就从磁盘读取对应AOF文件记录的命令,然后逐一执行来进行数据恢复;这就存在一个问题,当AOF日志很大的时候,数据恢复就会很慢,且其操作模式会对Redis的性能有所损耗。

Redis之AOF实现

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

Redis的AOF日志先执行后写入,主要基于以下考虑:

与MySQL和其他数据库的事务机制相比,Redis事务可以称为弱事务。如果事务中发生错误,将继续执行。如果事务失败,Redis不会回滚,对于此类事务,将有许多命令无法成功修改数据,如果先写日志,也会有很多无效的命令。

  1. 避免额外的检查开销:因为如果先将写操作命令记录到 AOF 日志里,再执行该命令的话,如果当前的命令语法存在问题,那么如果不进行命令语法检查,该错误的命令记就会被录到 AOF 日志里后,Redis 在使用日志恢复数据时,就可能会出错
  2. 不会阻塞当前写操作命令的执行:因为当执行命令操作成功后,才会将其命令写入到AOF日志中。当然,这样做也存在风险:
    1. 数据可能丢失:Redis执行写操作命令和日志记录是两个过程,当Redis还未执行完写操作命令时,此时宿主机发生宕机,那么这个数据可能就丢失了
    2. 可能阻塞其他操作:由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前命令的执行,但因为 AOF 日志也是在主线程中执行,所以当 Redis 把日志文件写入磁盘的时候,还是会阻塞后续的操作无法执行

AOF写回策略

Redis写入AOF日志步骤:

AOF日志步骤

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

Redis 提供了 3 种写回硬盘的策略,控制的就是上面说的第三步的过程。 在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:

  • Always(同步):每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘
  • Everysec(每秒):每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘
  • No(操作系统控制的回写):不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘

这三种方法都有缺点:

  • Always(同步),如果每次执行后命令都同步到瓷盘中,这会影响主线程的性能
  • Everysec(每秒)使用每秒回写一次的频率,以避免同步回写的性能开销。虽然它减少了对系统性能的影响,但如果发生中断,在最后一秒没有中断的命令操作仍将丢失
  • No(操作系统控制写回)。保存到磁盘的操作由操作系统控制。只要AOF中没有写回命令,一旦服务器停机,数据就会丢失;
写回策略 写回时间 优点 缺点
Always 同步 可靠性高,最大程度保证安全 性能开销大
Everysec 每秒 性能适中 宕机时有1s的数据丢失
No 操作系统控制 性能好 宕机时丢失数据较大

AOF日志过大会触发什么机制

AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。 如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。所以Redis提供了AOF重写机制,当AOF文件大小超过阈值,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。

重写机制,就是重写旧日志中的多个命令变为新日志中的一个命令。重写不会阻塞主线程,重写过程由后台子进程bgrewriteaof完成,目的是为了避免阻塞主线程,从而导致数据库性能下降。

AOF日志的重写机制

Redis 的重写 AOF 过程是由后台子进程 「bgrewriteaof 」来完成的,这么做的目的:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程

  • 子进程带有主进程的数据副本,这里使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。