lzh2nix / articles

用 issue 来管理个人博客
https://github.com/lzh2nix/articles
63 stars 13 forks source link

redis RDB实现 #143

Open lzh2nix opened 3 years ago

lzh2nix commented 3 years ago

何为RDB

RDB(Redis Database)是一种redis的数据持久化方式,类似是传统数据库里的快照(snapshot),只是某个时间点上的所有数据,当然如果只有rdb的话肯定无法保证数据的不丢失,在现实中都是配合AOF一起使用。但是rdb也有自己的优势

  1. RDB是某个时间点的快照,所以很适合备份+恢复的场景
  2. RDB采用异步进程的方式来串创建rdb文件,所以对主进程的性能完全没有影响
  3. 在主备场景下从节点可以使用主节点的rdb文件来快速恢复数据

RDB的触发时机

rdb的触发时机就是围绕着上面的几个优势项展开

RDB文件结构

不管是上面那种方式触发的备份都会走到 rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) 这个函数中生成统一的文件。rdb文件大概分为header/body/end 部分,其中header是和系统相关的一些信息包括version, aux, body是databse部分(copy server.dbnum里的所有数据),end部分主要是eof标志+checksum, 整体的结构如下 image

更详细rdb解析可以参考官方文档 https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format 其中aux为一些辅助信息,主要包含以下的内容:

/* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType). */
#define RDB_OPCODE_MODULE_AUX 247   /* Module auxiliary data. */
#define RDB_OPCODE_IDLE       248   /* LRU idle time. */
#define RDB_OPCODE_FREQ       249   /* LFU frequency. */
#define RDB_OPCODE_AUX        250   /* RDB aux field. */
#define RDB_OPCODE_RESIZEDB   251   /* Hash table resize hint. */
#define RDB_OPCODE_EXPIRETIME_MS 252    /* Expire time in milliseconds. */
#define RDB_OPCODE_EXPIRETIME 253       /* Old expire time in seconds. */
#define RDB_OPCODE_SELECTDB   254   /* DB number of the following keys. */
#define RDB_OPCODE_EOF        255   /* End of the RDB file. */

/* Module serialized values sub opcodes */
#define RDB_MODULE_OPCODE_EOF   0   /* End of module value. */
#define RDB_MODULE_OPCODE_SINT  1   /* Signed integer. */
#define RDB_MODULE_OPCODE_UINT  2   /* Unsigned integer. */
#define RDB_MODULE_OPCODE_FLOAT 3   /* Float. */
#define RDB_MODULE_OPCODE_DOUBLE 4  /* Double. */
#define RDB_MODULE_OPCODE_STRING 5  /* String. */

RDB 实现

上面提到的rdbSaveRio是整个rdb实现的核心,该函数负责的将rdb文件写入传入的rio中, 这里的rio可以是一个File *fp(rdb文件的生成)也可以是一个fd(通过socket在主备之间传输rdb文件), 以下是整体的流程: image 这里有几个点需要注意:

  1. rdbSaveBackground 中保存rdb文件是有独立的进程去完成
  2. 当前有rdb或aof进程存在的话不进行本次rdb文件的生成(rdbSaveToSlavesSockets除外)
  3. 写文件时先写临时文件然后rename成rdb文件