`
xpenxpen
  • 浏览: 725020 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

redis源码阅读笔记(9)——RDB,AOF持久化

阅读更多
1. 持久化
Redis提供了两种持久化数据到硬盘的方式。
RDB:数据库里所有记录的一个快照
AOF(append only file):原汁原味地记录了每次操作命令的历史记录,相当于一个log

如果还没了解过持久化功能的话,请先阅读Redis官网上的persistence手册中文翻译版

RDB以二进制方式存储,每条记录只记一次,文件大小更紧凑,这样恢复起来更快。但是写这个RDB需要时间,这样会造成宕机后数据丢失。
AOF以文本文件方式存储,把每条命令都记下来,会有冗余,但是写aof快,只要往文件末尾追加记录即可,可以使得宕机后可以恢复更多的数据。

接下来可以阅读Redis 设计与实现里的RDB和AOF部分。

2. RDB的save
    SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。
    BGSAVE 则 fork 出一个子进程,子进程调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。

2.1 redis代码核心点
我们看下BGSAVE的代码,主要关注如何fork子进程
//简化以后的流程 
int rdbSaveBackground(char *filename) {
    pid_t childpid;
    long long start;

    // 如果 BGSAVE 已经在执行,那么出错
    if (server.rdb_child_pid != -1) return REDIS_ERR;

    // ......

    if ((childpid = fork()) == 0) {
        int retval;
		
        /* Child */

        // 执行保存操作
        retval = rdbSave(filename);
		
	// ......
		
        // 向父进程发送信号
        exitFromChild((retval == REDIS_OK) ? 0 : 1);

    } else {

        /* Parent */

        // 如果 fork() 出错,那么报告错误
        if (childpid == -1) {
            return REDIS_ERR;
        }

        // 打印 BGSAVE 开始的日志
        redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);

        // 记录负责执行 BGSAVE 的子进程 ID
        server.rdb_child_pid = childpid;

        return REDIS_OK;
    }

    return REDIS_OK; /* unreached */
}

void exitFromChild(int retcode) {
    _exit(retcode); //_exit调用后,会发信号给父进程
}

解释一下,父进程fork出一个子进程。
子进程如果做完了,则调用_exit函数,即可通知到父进程。

如下代码则是父进程如何接收信号
/*  处理 BGSAVE 完成时发送的信号 */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {

    // BGSAVE 成功
    if (!bysignal && exitcode == 0) {
        redisLog(REDIS_NOTICE,
            "Background saving terminated with success");
        //......
    }
    // 更新服务器状态
    server.rdb_child_pid = -1;
}

//简化以后的流程, 这是 Redis 的时间中断器,每秒调用 server.hz 次。
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
	pid_t pid;
	// 接收子进程发来的信号,非阻塞
	if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {

		// BGSAVE 执行完毕
		if (pid == server.rdb_child_pid) {
			backgroundSaveDoneHandler(exitcode,bysignal);

		// BGREWRITEAOF 执行完毕
		} else if (pid == server.aof_child_pid) {
			backgroundRewriteDoneHandler(exitcode,bysignal);
		}
	}

}


以上代码便是核心关键,serverCron可以暂且理解成一个定时要执行的函数,wait3函数意思可以理解成查看一下子进程有没有完成,有了则调用backgroundSaveDoneHandler,没有的话不阻塞,继续干自己的活,过一段时间再来查看子进程是否完成。

如果看不懂,需要补习一下c的进程交互的知识(fork,wait,_exit)。

3. C语言里的fork函数
c语言:fork函数详解

3.1 写个程序进一步理解fork,wait,_exit

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main (void)
{
    pid_t fpid; //fpid表示fork函数返回的值
    int count=0;
    fpid=fork();
    if (fpid < 0)
        printf("error in fork!");
    else if (fpid == 0)
    {
        printf("i am the child process, my process id is %d, parent process id is %d\n",getpid(), getppid());
        printf("我是儿子\n");
        fflush(stdout);
        count++;
        sleep(10);
    }
    else
    {
        printf("i am the parent process, my process id is %d, parent process id is %d\n",getpid(), getppid());
        printf("我是孩子他爹\n");
        fflush(stdout);
        count++;

        int statloc;
        pid_t pid;

        while (1) {
            if ((pid = wait3(&statloc,1,NULL)) != 0) {
            //if ((pid = wait(&statloc)) != 0) {
                printf("收到儿子 %d的信号\n",pid);
                fflush(stdout);
                break;
            } else {
                printf("还没收到儿子 的信号\n");
                fflush(stdout);
                sleep(2);
            }
        }
    }
    printf("统计结果是: %d\n",count);
    fflush(stdout);
    return 0;
}


运行结果如下
i am the child process, my process id is 4260, parent process id is 6660
我是儿子
i am the parent process, my process id is 6660, parent process id is 1
我是孩子他爹
还没收到儿子 的信号
还没收到儿子 的信号
还没收到儿子 的信号
还没收到儿子 的信号
还没收到儿子 的信号
统计结果是: 1
还没收到儿子 的信号
收到儿子 4260的信号
统计结果是: 1

可以看到用了wait3的话,父亲每隔2秒看一下孩子,如果没有音讯,可以做自己的事情(非阻塞的),过一会儿再来看看。
而如果把wait3改成wait的话(把代码里wait3那行注释掉,wait那行注释去掉)
运行结果如下
i am the child process, my process id is 10904, parent process id is 9056
我是儿子
i am the parent process, my process id is 9056, parent process id is 1
我是孩子他爹
统计结果是: 1
收到儿子 10904的信号
统计结果是: 1

可以看到父亲傻傻地等待孩子的音讯(阻塞的)等了10秒,这之间他不能做别的事情。

4. RDB和AOF的格式
这一部分自己跑个实例,然后打开dump.rdb和appendonly.aof,对照网上的资料或是协议分析一下就可以了,比较枯燥,不再赘述。

5. LZF压缩
5.1 Redis里的LZF
RDB因为存的是二进制,所以可以对长的字符串做压缩。如果字符串长度大于20,并且服务器开启了LZF压缩功能,那么保存压缩之后的数据。
用到的类库是liblzf
redis把以下4个源文件原封不动的拷过来了。
lzf_c.c
lzf_d.c
lzf.h
lzfP.h

5.2 Java里的LZF
java也有lzf的类库,可参考如下网址:
https://github.com/ning/compress
https://github.com/ning/jvm-compressor-benchmark/wiki

6. CRC校验
6.1 Redis里的CRC
RDB文件的末尾8个字节是CRC校验和(循环冗余校验).
算法都在下面几个文件里
crc16.c
crc64.c
crc64.h

redis采用了crc-64-jones算法,
rio.h中的定义
struct _rio {
    // 校验和计算函数,每次有写入/读取新数据时都要计算一次
    void (*update_cksum)(struct _rio *, const void *buf, size_t len);
    // 当前校验和
    uint64_t cksum;
};

typedef struct _rio rio;

//将 buf 中的 len 字节写入到 r 中。
static inline size_t rioWrite(rio *r, const void *buf, size_t len) {
    while (len) {
        size_t bytes_to_write = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;
        // 如果crc函数指针被赋值过,则调用它更新crc值
        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_write);
        if (r->write(r,buf,bytes_to_write) == 0)
            return 0;
        buf = (char*)buf + bytes_to_write;
    }
    return 1;
}

如上定义了一个函数指针void (*update_cksum),
rioWrite可以理解成写rdb文件时会调用的函数,每调用一次,它就会去调用函数指针,以这样的方式来更新crc值:
r->update_cksum()

rio.c
/*
 * 通用校验和计算函数
 */
void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {
    r->cksum = crc64(r->cksum,buf,len);
}

这个函数就是简单的调用一下crc64.c里写的crc算法

rdb.c中
int rdbSave(char *filename) {
    // 设置校验和函数,如果需要校验,则把函数指针赋值给rdb.update_cksum
    if (server.rdb_checksum)
        rdb.update_cksum = rioGenericUpdateChecksum;
    // ......
    cksum = rdb.cksum;
    rioWrite(&rdb,&cksum,8);
}

最后可以看到rdbSave的时候会去调用rioWrite,而最后则是写入crc校验和。

6.2 Java里的CRC
JDK里也有crc的算法实现,不过是32位的
java.util.zip.CRC32
java.util.zip.Adler32

Adler-32 校验和几乎与 CRC-32 一样可靠,但是能够更快地计算出来。
分享到:
评论

相关推荐

    Redis 持久化之RDB和AOF.doc

    Redis 持久化是确保数据安全的重要机制,它提供了两种主要的方法:RDB(Redis Database)和 AOF(Append Only File)。RDB 是一种快照式的持久化方式,而 AOF 则记录每次写操作的日志。 RDB 持久化在特定条件下将...

    Redis持久化 - RDB和AOF

    “Redis持久化 - RDB和AOF” Redis持久化是指将数据库中的数据保存到永久存储设备中,以避免数据丢失。Redis提供了两种持久化方式:RDB(快照方式)和AOF(写日志方式)。 RDB(Redis Database)是一种快照方式的...

    部署安装Redis及RDB、AOF持久化验证.md

    部署安装Redis及RDB、AOF持久化验证.md

    Redis的安装/连接/Redis中的五种数据累心的基本操作/Redis的持久化方案-Rdb+AOF

    Redis还可以结合RDB和AOF两种方式,实现更灵活的持久化策略,以平衡数据安全和性能需求。 【NoSQL数据库的优势与应用场景】 NoSQL 数据库相比关系型数据库,具有以下优势: 1. 非结构化数据处理:NoSQL 支持处理...

    Redis持久化机制思维导图完整版

    混合持久化(RDB + AOF)混合持久化结合了RDB持久化和AOF持久化的优点,可以在保证数据安全性的同时,提供较快的数据加载速度。在这种持久化方式下,Redis会同时生成RDB文件和AOF文件。当Redis重新启动时,优先使用...

    狂神redis源码笔记.rar

    2. **持久化**:Redis提供RDB(快照)和AOF( Append Only File)两种持久化方式,确保在服务器重启后能恢复数据。RDB在指定时间间隔生成数据快照,而AOF记录所有写操作日志。 3. **主从复制**:Redis支持主从复制...

    09.图解分析redis的RDB和AOF两种持久化机制的工作原理.zip

    Redis的RDB和AOF持久化机制分别提供了不同的数据安全保障。RDB通过定期快照,适合对数据一致性要求不那么高的场景,而AOF则更适合需要保证数据完整性的应用。选择合适的持久化策略,需要权衡数据丢失的风险、恢复...

    数据不丢失:Redis RDB与AOF持久化深度解析

    2. **持久化**:尽管 Redis 是一个内存数据库,但它提供了持久化机制,可以将内存中的数据保存到磁盘,防止数据丢失。 3. **支持事务**:Redis 支持简单的事务功能,可以保证操作的原子性。 4. **丰富的数据类型**:...

    Redis的持久化方案

    AOF持久化方案是指记录每一个对Redis数据库进行修改的命令到一个日志文件中,并在Redis重启时通过回放这些命令来恢复数据。AOF能够提供更高的数据安全性,可以通过配置选择不同的fsync策略,如每次写入后同步到磁盘...

    Redis两种持久化方案RDB和AOF详解

    **AOF持久化**则是另一种持久化策略,它记录了所有的写操作日志到一个`.aof`文件。Redis重启时会读取这些日志重新执行,从而恢复数据。AOF默认是关闭的,可以通过修改配置文件开启。AOF的优点在于可以提供更好的数据...

    Redis的持久化方案.pdf(两种持久化方案:RDB 和 AOF,共15页)

    - `appendonly`: 是否启用AOF持久化,默认为no。 - `appendfsync`: 写入策略,默认为everysec,表示每秒同步一次。 - `auto-aof-rewrite-percentage` 和 `auto-aof-rewrite-min-size`: 控制何时自动触发AOF重写。 #...

    RDB持久化机制: - RDB默认开启 - RDB存储二进制数据

    - RDB持久化机制: ...- AOF持久化机制: - AOF默认关闭,需要将appendonly yes手动开启 - RDB默认持久化日志文件,将每次写操作的命令持久化到本地文件中,在持久化和读取持久化文件时,相对RDB较慢

    redis持久化方式

    为了保证数据的安全性和持久性,Redis 提供了两种主要的持久化机制:RDB 快照(Redis Database Backup)和 AOF(Append Only File)。本文将详细介绍这两种持久化方式的原理、配置方法及其各自的优缺点。 #### RDB ...

    Redis持久化RDB和AOF区别详解

    **AOF持久化**: AOF(Append Only File)记录了所有对数据库的写操作,以命令追加形式保存在日志文件中。当Redis重启时,会重新执行AOF文件中的所有命令来恢复数据。AOF有三种同步策略: 1. `always`:每次写操作...

    【大厂面试】Redis 持久化AOF、RDB概念总结

    AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以 redis 协议追加保存每次写的操作到文件末尾,redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积...

    Redis Windows源码

    Redis支持两种持久化方式:RDB(快照)和AOF(Append Only File)。RDB定期保存当前数据库状态到磁盘,而AOF记录所有写操作日志。可以根据需求选择合适的持久化策略,保证数据安全性。 7. 主从复制与集群: Redis...

    AOF 持久化.pdf

    AOF持久化的方法使得在Redis停止工作时,可以通过重新执行这些命令来恢复数据。AOF提供了比RDB更高的数据安全性,因为它可以在不中断当前写操作的情况下,以很小的数据丢失风险来持久化数据。 从上面的描述中我们...

Global site tag (gtag.js) - Google Analytics