Redis持久化

数据持久化

RDB持久化

  • RDB持久化通过Save或者BgSave命令来触发,其中Save命令使用服务器进程会阻塞客户端的读写命令,而BgSave命令则是派生了一个子进程去处理保存的操作,客户端在这期间仍然可以进行读写操作。

  • RDB和AOF同时开启的情况下,服务器会优先读取AOF文件,如没有开启AOF持久化才会读RDB文件。

  • 在BgSave执行时,为避免产生竞争条件,BgSave命令和Save命令将被服务端拒绝。

  • 在BgSave执行时,BgReWriteAof也将延迟到BgSave命令完成后进行。而BgReWriteAof执行时,BgSave将直接被拒绝,这是因为这两个都是高IO操作,虽然不产生冲突,但对性能有影响。

  • RDB文件载入时,将阻塞服务端进程。

  • BgSave可以通过配置自动触发,主要靠RedisServer的参数dirty(上次保存后修改次数)、saveParams(触发条件,数组)、lastSave(上次保存时间)。saveParams可配置多长时间内修改次数达多少次触发BgSave命令,长度为3。具体结构如代码所示

    class SaveParam {
        // 多少次修改触发
        int changes;
        // 多长时间触发
        int time;
    }
    
    class RedisServer {
        // 自上次保存修改次数
        int dirty;
    	// 上次保存时间戳
        long lastSave;
        // 长度为3
        SaveParam[] saveParams;
    }
    
    
    

    Redis服务器的ServerCron函数每默认100ms工作一次,其中便会检查saveParams是否满足并触发对应命令。

  • RDB文件结构

    class Database {
        // 数据库index
        private int databaseNum;
        // 数据库中的键值对
        private List<Entry> entries;
        
        public Database {
            exex("select " + databaseNum);
        }
        
        // 键值对对象
        class Entry {
            // 保存的value对象的类型 RedisRDBType(String、List、Hash、Set、ZSet对应的数据结构实现)
            private Class valueClazz;
            // 键值对的key,always String
            private String key;
            // 键值对的value,可变类型
            private Object value;
            // 是否设置过过期
            private byte expire;
            // 过期时间,如果有
            private int expireMs;
        }
    }
    
    class RdbFile {
        // 前缀
    	private static final String prefix = "REDIS";
        // 版本
        private int version;
        // 数据库数据
        private List<Database> databases;
        // 结束标志位
        private int eof;
        // checksum
        private String checkSum;
        
    }
    

AOF持久化

AOF持久化分为命令追加、文件写入、文件同步三个步骤。

  1. 命令追加。当服务器执行完一个写命令,会以协议格式添加到RedisServer的aofBuf缓冲区末尾。

    class RedisServer {
        // aof缓冲区
        private String aofBuf;
    }
    
  2. 写入和同步。Redis的服务器进程是一个事件循环,这个循环中的文件事件负责接收客户端请求并响应,时间时间则是执行serverCron函数这样定时运行的行数。在处理文件事件时可能存在redisServer的aofBuf写入,所以需要进行判断是否写入aofBuf的内容到文件中。伪代码如下

    while(true) {
        // 处理文件事件,接收命令和响应,可能存在redisServer的aofBuf写入
        processFileEvents();
        // 处理时间事件
        processTimeEvents();
        // 由于可能存在aofBuf写入,所以需要进行判断是否写入aofBuf的内容到文件中
        flushApendOnlyFile();
    }
    

    flushApendOnlyFile函数的行为由服务端配置决定包含三种

    • always:aofBuf全部写入并同步

    • everysec(默认):aofBuf全部写入,同时每秒进行同步,由单独线程进行同步操作

    • no:aofBuf全部写入单不同步

AOF通过读取文件还原命令并通过伪客户端执行的方法对数据库状态进行还原。

AOF重写用于解决时间流逝导致AOF文件不断增长的膨胀问题。这个功能是通过读取服务器现有状态然后生成aof文件遵守的原则是尽可能小

  • bg_rewrite_aof通过派生子进程进行aof的重写,但重写的同时会响应客户端请求,可能会造成数据不一致的情况,为了解决这个问题,redisServer中维护了一个aofRewriteBuf的缓冲区在进行aofRewrite时响应的请求会写入到aofRewriteBuf中。
  • 在bg_rewrite_aof完成时,会向主程序发一个信号,主程序接收到信号后首先将aofRewriteBuf中的内容重新写入到aof文件中,此时的aof文件便和最新的数据库状态保持一致,然后原子的覆盖旧的aof文件。此过程是在redisServer主进程中进行的,所以会阻塞请求。
posted @ 2021-09-08 12:47  M104  阅读(33)  评论(0编辑  收藏  举报