Redis中的RDB持久化和AOF持久化(一)

来源:互联网 发布:局域网屏幕广播软件 编辑:程序博客网 时间:2024/05/17 12:56

概述

Redis是一种内存数据库,运行时数据和状态都保存在内存中,为了避免服务器进程结束而导致的数据丢失,需要将数据保存到磁盘上。Redis提供了两种策略,分别是RDB持久化和AOF持久化。本文先介绍RDB持久化。

RDB持久化

手动创建RDB文件的两个命令是SAVE和BGSAVE,他们的区别是SAVE在主进程中进行文件写入,保存时会阻塞主进程,使其不能执行任何其他操作。BGSAVE是fork一个子进程来保存,具体过程如下:

  • redis调用fork,现在有了子进程和父进程。
  • 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
  • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
/* * 使用子进程保存数据库数据,不阻塞主进程 */int rdbSaveBackground(char *filename) {    pid_t childpid;    long long start;    if (server.rdb_child_pid != -1) return REDIS_ERR;        // 修改服务器状态    server.dirty_before_bgsave = server.dirty;    // 开始时间    start = ustime();    // 创建子进程    if ((childpid = fork()) == 0) {        int retval;        /* Child */        // 子进程不接收网络数据        if (server.ipfd > 0) close(server.ipfd);        if (server.sofd > 0) close(server.sofd);        // 保存数据        retval = rdbSave(filename);        if (retval == REDIS_OK) {            size_t private_dirty = zmalloc_get_private_dirty();            if (private_dirty) {                redisLog(REDIS_NOTICE,                    "RDB: %lu MB of memory used by copy-on-write",                    private_dirty/(1024*1024));            }        }                // 退出子进程        exitFromChild((retval == REDIS_OK) ? 0 : 1);    } else {        /* Parent */        // 记录最后一次 fork 的时间        server.stat_fork_time = ustime()-start;        // 创建子进程失败时进行错误报告        if (childpid == -1) {            redisLog(REDIS_WARNING,"Can't save in background: fork: %s",                strerror(errno));            return REDIS_ERR;        }        redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);        // 记录保存开始的时间        server.rdb_save_time_start = time(NULL);        // 记录子进程的 id        server.rdb_child_pid = childpid;        // 在执行时关闭对数据库的 rehash        // 避免 copy-on-write        updateDictResizePolicy();        return REDIS_OK;    }    return REDIS_OK; /* unreached */}

除了手动保存之外,RDB还支持自动间隔保存。如果我们用如下命令设置服务器:

save 300 1save 800 40

表示只要满足以下两个条件之一,就会执行BGSAVE命令

  • 服务器在300秒之内,对数据库至少进行了1次修改
  • 服务器在800秒之内,对数据库至少进行了40次修改

在服务器的结构体redisServer的saveparams数组中,保存了以上两个执行条件。另外还保存着一个dirty计数器,以及lastsave属性。dirty计数器记录了距离上一次保存之后,服务器对数据库进行了多少次修改。lastsave记录了上一次保存的时间。在周期性调用的函数serverCron中,每次都会检查dirty和lastsave,如果满足了预设的保存条件就调用BGSAVE来保存。

以下是serverCron函数中的一段代码:

        /* If there is not a background saving/rewrite in progress check if         * we have to save/rewrite now */         // 如果有需要,开始 RDB 文件的保存         for (j = 0; j < server.saveparamslen; j++) {            struct saveparam *sp = server.saveparams+j;            if (server.dirty >= sp->changes &&                server.unixtime-server.lastsave > sp->seconds) {                redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",                    sp->changes, sp->seconds);                rdbSaveBackground(server.rdb_filename);//调用BGSAVE函数                break;            }         }

RDB的优势

  • 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。比如你可能打算没1天归档一些数据。
  • 方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。

RDB的劣势

  • 如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
  • 每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。




0 0
原创粉丝点击