Redis -- 持久化

持久化机制

  1. Redis有两种持久化机制,一种是快照,另一种是AOF日志
  2. 快照是一次全量备份AOF日志是连续的增量备份
  3. 快照内存数据的二进制序列化形式,在存储上非常紧凑AOF日志记录的是内存数据修改的指令记录文本
    • AOF日志长期的运行过程中会变得很庞大,数据库重启时需要加载AOF日志进行指令重放,时间很漫长
    • 因此需要定期进行AOF重写,给AOF日志进行瘦身

快照原理

  1. Redis是单线程程序,要同时负责多个客户端套接字的并发读写操作内存数据结构的逻辑读写
  2. 服务线上请求的同时,Redis还需要进行内存快照
    • 内存快照要求Redis必须进行文件IO操作,可文件IO操作是不能使用多路复用API
    • 存在的挑战
      • 单线程同时在服务线上的请求还要进行文件IO操作,而文件IO操作会严重拖垮服务器请求的性能
      • 为了不阻塞线上的业务,需要边持久化边响应客户端请求
    • Redis使用操作系统的多进程COW(Copy On Write)机制来实现快照持久化

Fork多进程

  1. Redis在持久化时会调用glibcfork函数产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求
  2. 子进程刚产生时,和父进程共享内存里的代码段数据段,在进程分离那一瞬间,内存的增长几乎没有明显变化
  3. 子进程做数据持久化,不会修改现有的内存数据结构,只是对数据结构进行遍历读取,然后序列化写到磁盘
  4. 父进程必须持续服务客户端请求,然后对内存数据结构进行不间断地修改
    • 此时会使用操作系统的COW机制来进行数据段页面的分离
    • 当父进程对数据段中的一个页面进行修改时,会将被共享的页面复制一份分离出去,然后对这个复制的页面进行修改
    • 此时子进程相应的页面没有变化,还是子进程产生时那一瞬间的数据,因此称为快照
  5. 随着父进程修改操作的持续进行,越来越多的共享页面被分离出去,内存会持续增长,但也不会超过原有数据内存大小的两倍
1
2
3
4
5
6
7
8
# 父进程里返回子进程的pid,子进程里返回0,如果操作系统内存资源不足,pid为负,表示fork失败
pid = os.fork()
if pid > 0:
handle_client_requests() # 父进程继续处理客户端请求
if pid == 0:
handle_snapshot_write() # 子进程处理快照写磁盘
if pid < 0:
# fork error

AOF原理

  1. AOF:Append Only File
  2. AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录
  3. 执行指令 -> 日志存盘
    • Redis会在收到客户端修改指令后,进行参数校验进行逻辑处理后,如果没问题,会立即将该指令文本存储到AOF日志中
  4. Redis在长期运行的过程中,AOF日志会越来越大
    • 如果实例宕机重启,重放整个AOF日志会非常耗时,导致长时间Redis无法对外提供服务,所以需要对AOF日志瘦身

AOF重写

  1. Redis提供了bgrewriteaof指令用于对AOF日志进行瘦身
  2. 原理
    • 开辟一个子进程对内存进行遍历转换成一系列Redis操作指令,序列化到一个新的AOF日志文件
    • 序列化完毕后再将操作期间发生的增量AOF日志追加到新的AOF日志文件中,追加完成后立马替换旧的AOF日志

fsync

  1. AOF日志是以文件的形式存在的,当程序对AOF日志文件进行写操作
    • 实际上是将内容写到内核为文件描述符分配的一个内存缓冲区中,然后内核会异步将脏数据刷回磁盘
    • 如果机器突然宕机,AOF日志内容可能还没来得及完全刷到磁盘上,此时会出现日志丢失
  2. glibc提供了fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷新到磁盘
    • 只要Redis进程实时调用fsync函数就可以保证AOF日志不丢失,但fsync是一个磁盘IO操作,非常慢
    • 在生产环境中,Redis通常每隔1S(可配置)左右就执行一次fsync操作,在保持高性能的同时,尽可能少丢失数据
  3. Redis提供了另外两种策略
    • 一种是永远不fsync,让操作系统来决定何时同步磁盘,很不安全
    • 一种是每个指令都执行fsync,很慢

运维

  1. 快照是通过开启子进程的方式进行的,是一个比较耗资源的操作
    • 遍历整个内存大量写磁盘会加重系统负载
  2. AOF的fsync是一个耗时的磁盘IO操作,会降低Redis性能,同时会增加系统IO负担
  3. 通常Redis主节点不会进行持久化操作,持久化操作主要在从节点进行
    • 这里的从节点指的是备份节点,没有来自客户端请求的压力

混合持久化

  1. 重启Redis,很少使用rdb来恢复内存状态,因为会丢失大量数据
    • 通常使用AOF日志重放,但重放AOF日志相对rdb来说要慢很多
  2. Redis 4.0为了解决该问题,引入了混合持久化
    • rdb文件的内容增量的AOF日志文件存在一起
    • 增量AOF日志:自持久化开始到持久化结束这段时间发生的增量AOF日志,通常这部分AOF日志很小
    • Redis重启时,先加载rdb的内容,再重放增量AOF日志,重启效率大幅提升
0%