Redis中的RDB持久化

34 阅读6分钟

Redis中的RDB持久化

说明

本文是学习黄健宏《Redis设计与实现》关于RDB持久化中,阅读源码之后的一点心得体会。

该书成文于2014年,使用的版本是2.9,笔者使用的是6.0.6,其中会有差异,可能概念的描述会有所过期,还望可以留言指正。

定义

我们定义“数据库状态”如下:

某一刻服务器中的非空数据库以及其中的键值对。

数据库状态相当于某一时刻redis内存中需要持久化数据的快照。

将数据库状态完整保存到磁盘的方式叫做RDB持久化。

rdb文件的生成

RDB持久化的时间点有两种方式指定

  1. 用户手动调用save或者bgsave
  2. redis程序定期调用

1.手动调用生成:

手动调用save: redis服务器接收到指令后,会开始将数据写入到RDB文件,同时阻塞外界一切请求,是一个阻塞类型的调用。 bgsave: redis收到指令后,会fork一个子进程出来,子进程中将数据写入到rbd文件,这时父进程还可以处理外界请求。

但是在执行bgsave期间,收到save或者bgsave会直接拒绝返回错误,收到bgrewriteaof,此命令不会拒绝,但不会同时执行,bgrewriteaof的过程会在bgsave结束后调用,bgrewriteaof用于生成aof持久化文件。

2.自动保存:

可以在配置文件中这样设置

save <seconds> <changes>

表示在seconds的范围时间内,至少changes次操作以后,会触发一下同步。

默认为

save 900 1
save 300 10
save 60 10000

表示在900秒内一次|300秒内10次|60秒内10000次

自动保存的实现细节:

struct redisServer {
    // ...
    long long dirty;                /* Changes to DB from the last save */
    struct saveparam *saveparams;   /* Save points array for RDB */
    int saveparamslen;              /* Number of saving points */
    time_t lastsave;                /* Unix time of last successful save */
};

struct saveparam {
    time_t seconds;
    int changes;
};
  • lastsave记录上一次rdb成功保存的时间
  • dirty记录上一次成功保存之后,数据库中的数据经历过多少次变化
  • saveparams记录了服务端关于自动保存的参数配置

redis服务器默认每100毫秒执行一次定时任务,下述为定时任务中的一段代码逻辑

        for (j = 0; j < server.saveparamslen; j++) {
            struct saveparam *sp = server.saveparams+j;

            /* Save if we reached the given amount of changes,
             * the given amount of seconds, and if the latest bgsave was
             * successful or if, in case of an error, at least
             * CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed. */
            if (server.dirty >= sp->changes &&
                server.unixtime-server.lastsave > sp->seconds &&
                (server.unixtime-server.lastbgsave_try >
                 CONFIG_BGSAVE_RETRY_DELAY ||
                 server.lastbgsave_status == C_OK))
            {
                serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
                    sp->changes, (int)sp->seconds);
                rdbSaveInfo rsi, *rsiptr;
                rsiptr = rdbPopulateSaveInfo(&rsi);
                rdbSaveBackground(server.rdb_filename,rsiptr);
                break;
            }
        }

可以看到,每次会取出保存的参数进行对比,成立的话会在fork一个子进程出来进行同步。

写入时会先写一个temp文件,然后再进行rename操作

temp文件格式如下:

snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());

RDB文件的格式:

RDB文件是一个按照固定格式存储的二进制数据,具体格式本文不做介绍。 可以使用od -cx dump.rdb来打印可视化的rdb文件

zhengshi@bj-dl-003:~/software/redis/data$ od -cx dump.rdb 
0000000   R   E   D   I   S   0   0   0   9 372  \t   r   e   d   i   s
           4552    4944    3053    3030    fa39    7209    6465    7369
0000020   -   v   e   r 005   6   .   0   .   6 372  \n   r   e   d   i
           762d    7265    3605    302e    362e    0afa    6572    6964
0000040   s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 302
           2d73    6962    7374    40c0    05fa    7463    6d69    c265
0000060 020   }   q   e 372  \b   u   s   e   d   -   m   e   m 302 310
           7d10    6571    08fa    7375    6465    6d2d    6d65    c8c2
0000100   A  \f  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
           0c41    fa00    610c    666f    702d    6572    6d61    6c62
0000120   e 300  \0 376  \0 373 001  \0  \0 001   n 302   @ 342 001  \0
           c065    fe00    fb00    0001    0100    c26e    e240    0001
0000140 377 231 244 206 323 276 350 251   L
           99ff    86a4    bed3    a9e8    004c
0000151

rdb中对字符串数据的压缩:

可以在配置文件中修改下述字段去开启或者关闭压缩,默认是开启

# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes

开启的情况下,大于20字节的字符串会进行压缩。小于20字节的字符串会以原样显示。

RDB文件的载入

RDB文件的生成有主动和被动方式两种,但是载入只有在服务器启动时,程序自动载入,没有手动载入的办法。

在载入的过程中,会阻塞服务器的请求,载入后才开始处理。

如果服务器已经开启了AOF持久化功能,那redis进程会优先加载aof的持久化文件,不会加载rdb的。 只有AOF功能关闭时,才会使用RDB文件加载。

注意点

  • rdb持久化和aof持久化可以同时打开
  • aof持久化打开时,如果对应的aof文件不存在,会创建一个新的,不会使用rdb的

所以,一旦rdb持久化和aof持久化同时打开, rdb中的数据就会完全被aof中的数据覆盖,如果一开始没有aof的文件,那rdb中的数据相当于会完全清空!!!

那如何将rdb的数据转到aof的文件中?

    1. 先切断网络
    1. 执行bgrewriteaof
    1. 恢复网络
    1. 再用aof持久化方式启动。

总结

  • RDB 文件用于保存和还原 Redis 服务器所有数据库中的所有键值对数据。
  • SAVE 命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
  • BGSAVE 令由子进程执行保存操作,所以该命令不会阻塞服务器。
  • 服务器状态中会保存所有用 save 选项设置的保存条件,当任意一个保存条件被演足时,服务器会自动执行 BGSAVE 命令。
  • RDB 文件是一个经过压缩的二进制文件,由多个部分组成。
  • 对于不同类型的键值对,RDB 文件会使用不同的方式来保存它们。