redis源码学习5 持久化与I/O 等

涉及持久化处理,I/O 操作

先列模块

  • db.c c语言层的数据库API
  • lazyfree.c 惰性释放空间,通过后台线程
  • aof.c append only file 持久化存储-逐条命令
  • rdb.c 持久化存储-快照
  • childinfo.c aof和rdb 的子进程操作
  • rio.c 面向具体 I/O 设备的数据流的抽象接口
  • syncio.c 同步的socket 和文件I/O 操作
  • bio.c 后台 I/O, 一些并行处理的操作

db.c c语言层的数据库API

  • `lookupKeyRead()` 和 `lookupKeyWrite()` 得到关联到指定键的值的指针, 或若键不存在时返回 `NULL`

  先 `expireIfNeeded()` 处理是否超时,然后 `lookupKey()` 查找关联的值的指针

  • `dbAdd()` 及其高级接口 `setKey()` 添加新键
    •  增加值的引用
    •  通知在监控这个键的客户端
    •  重置超时
  • `dbDelete()` 删除键与关联的值。若惰性释放空间,会调用lazyfree.c 里的函数加入后台线程去处理
  • `emptyDb()` 清空整个db,同样支持惰性释放

aof.c append only file 持久化存储-逐条命令

把每条命令都记录下来。
开一个后台线程写文件。会调用fsync 直接写进文件
重建会利用 `fork()` 处理,后续小于一定量的命令也会通过管道传输重写进去。支持 rdb 前序模式,更省空间

rdb.c 持久化存储-快照

开启rdb后符合条件就会后台存储一次,主要依据是 `server.dirty` 和 时间
写快照会调用 `fork()` 处理

注意

server.c 里的 `serverCron()` 会检查aof 和rdb 的子进程


rio.c 面向具体 I/O 设备的数据流的抽象接口

有三种设备
  • 读写内存buffer, 见 `rioInitWithBuffer()`
  • 读写硬盘文件, 见 `rioInitWithFile()`
  • 写socket,见 `rioFdsetWrite()`, 通常是从节点

syncio.c 同步的socket 和文件I/O 操作

redis 大部分 I/O 是非阻塞,但有些确实需要阻塞的同步操作
调用了 ae.c 里的 `aeWait()` 来等待


bio.c 后台 I/O, 一些并行处理的操作

有 BIO_CLOSE_FILE, BIO_AOF_FSYNC, BIO_LAZY_FREE 三个
通过新建线程处理。同一操作加锁。
下面给个线程操作简化版例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct bio_job {
    void *arg1; /* 参数指针 */
}bio_job;

typedef struct stack{
    int size;
    struct bio_job *data[100];
}stack;

static pthread_mutex_t bio_mutex;
static pthread_cond_t bio_newjob_cond;
static pthread_cond_t bio_step_cond;
static stack bio_jobs;
static int bio_pending;

void *bioProcessBackgroundJobs(void *arg);

/* 足够的栈大小 */
#define REDIS_THREAD_STACK_SIZE (1024*1024*4)

void stackAdd(stack *st, struct bio_job *item)
{
    st->data[st->size++] = item;
}

struct bio_job *stackPop(stack *st)
{
    return st->data[--st->size];
}

/* 初始化后台线程 */
void bioInit(void) {
    pthread_attr_t attr;
    pthread_t thread;
    size_t stacksize;

    /* Initialization of state vars and objects */
    pthread_mutex_init(&bio_mutex,NULL);
    pthread_cond_init(&bio_newjob_cond,NULL);
    pthread_cond_init(&bio_step_cond,NULL);
    bio_jobs.size = 0;
    bio_pending = 0;

    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr,&stacksize);
    if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */
    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
    pthread_attr_setstacksize(&attr, stacksize);

    /* 创建线程 */
    if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,NULL) != 0) {
        printf("Fatal: Can't initialize Background Jobs.");
        exit(1);
    }
}

void bioCreateBackgroundJob(void *arg1) {
    struct bio_job *job = malloc(sizeof(*job));

    job->arg1 = arg1;
    pthread_mutex_lock(&bio_mutex);
    stackAdd(&bio_jobs,job);
    bio_pending++;
    pthread_cond_signal(&bio_newjob_cond);
    pthread_mutex_unlock(&bio_mutex);
}

void doJob(char *s)
{
    sleep(3); /* 模拟非常耗时的操作 */
    printf(s);
    free(s);
}

void *bioProcessBackgroundJobs(void *arg) {
    struct bio_job *job;
    sigset_t sigset;

    /* 设置可在任何时候被杀死 */
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    pthread_mutex_lock(&bio_mutex);
    /* 屏蔽 SIGALRM 使只有主线程能接受 watchdog 信号. */
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
    if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
        printf("Warning: can't mask SIGALRM in bio.c thread: %d", errno);

    while(1) {
        /* 等待触发 */
        if (bio_jobs.size == 0) {
            pthread_cond_wait(&bio_newjob_cond,&bio_mutex);
            continue;
        }

        job = stackPop(&bio_jobs); /* 取一个任务 */
        /* 处理任务前先解锁 */
        pthread_mutex_unlock(&bio_mutex);

        /* 做事 */
        doJob((char *)job->arg1);
        free(job);

        /* 若需要,解锁 bioWaitStepOfType() 的等待 */
        pthread_cond_broadcast(&bio_step_cond);

        /* 在进入新循环前加锁, 如果没有任务了就在 pthread_cond_wait() 里阻塞 */
        pthread_mutex_lock(&bio_mutex);
        bio_pending--;
    }
}

/* 等待一些步骤 */
int bioWaitStepOfType() {
    int val;
    pthread_mutex_lock(&bio_mutex);
    val = bio_pending;
    if (val != 0) {
        pthread_cond_wait(&bio_step_cond,&bio_mutex);
        val = bio_pending;
    }
    pthread_mutex_unlock(&bio_mutex);
    return val;
}

int main(int argc,char *argv[])
{
    char *s1, *s2;
    s1 = malloc(20);
    s2 = malloc(20);
    strcpy(s1, "hello aa\n");
    strcpy(s2, "oh no\n");

    bioInit();
    bioCreateBackgroundJob(s1);
    sleep(1);
    bioCreateBackgroundJob(s2);

//    sleep(1);
    bioWaitStepOfType(); /* 等一步 */
    printf("main end\n");

    sleep(8);
    return 0;
}



你可能感兴趣的:(redis源码学习)