文章目录

  • 前言
  • 一、异步线程
  • 二、代码实现
  • 总结



前言

Redis BIO其实就是用于异步操作的,理解这点非常重要。

redis的bitcount多快_redis


Redis除了主线程外,还有3条异步线程,用于异步操作,来减少主线程的阻塞。

一、异步线程

  • BIO_CLOSE_FILE : 用与处理aof重写时,对于旧的aof文件执行close()函数关闭该文件。
  • BIO_AOF_FSYNC : 在需要aof文件同步的时候,利用这个线程来执行fsync()函数来同步aof文件。
  • BIO_LAZY_FREE : 释放空间。

数据库key的异步删除就是用BIO_LAZY_FREE线程来执行的。了解同步和异步删除可以看 文章链接

二、代码实现

static pthread_mutex_t bio_mutex[BIO_NUM_OPS];
// 保存每个类型线程消费的任务列表(如果列表为空,这个线程则什么都不做)。
// 每个任务列表都是用列表存储的。
static list *bio_jobs[BIO_NUM_OPS];
/* 保存每个类型线程待消费的任务数 */
static unsigned long long bio_pending[BIO_NUM_OPS];
/* 服务器启动时会初始化bio线程 */
void bioInit(void) {
    pthread_attr_t attr;
    pthread_t thread;
    int j;
    for (j = 0; j < 3; j++) {
        // 每个类型的线程等待执行的任务列表
        bio_jobs[j] = listCreate();
        // 待消费的任务数
        bio_pending[j] = 0;
    }
    /* 创建异步线程(3个线程) */
    for (j = 0; j < 3; j++) {
        void *arg = (void*)(unsigned long) j;
        pthread_create(&thread, &attr, bioProcessBackgroundJobs, arg);
        bio_threads[j] = thread;
    }
}
// 创建异步线程消费的任务
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
    struct bio_job *job = zmalloc(sizeof(*job));
    job->time = time(NULL);
    job->arg1 = arg1;
    job->arg2 = arg2;
    job->arg3 = arg3;
    // 加锁,因为和异步线程都会操作这个链表,这里添加任务节点。
    // 定时任务是取出任务节点进行消费。
    pthread_mutex_lock(&bio_mutex[type]);
    // 添加到任务链表里,这里注意bio_jobs是个数组,每个index下会保存一个类型的任务链表。
    listAddNodeTail(bio_jobs[type], job);
    // 增加待消费的任务数
    bio_pending[type]++;
    // 如果异步线程正在睡眠,则唤醒异步线程
    pthread_cond_signal(&bio_newjob_cond[type]);
    // 释放锁
    pthread_mutex_unlock(&bio_mutex[type]);
}
// 异步线程的执行函数,在函数里面会进行任务的消费
void *bioProcessBackgroundJobs(void *arg) {
    struct bio_job *job;
    unsigned long type = (unsigned long) arg;
    sigset_t sigset;
    // 加锁
    pthread_mutex_lock(&bio_mutex[type]);
    while(1) {
        listNode *ln;
        // 如果该类型线程的待消费任务数为0,则进行等待主线程增加任务时进行唤醒
        if (listLength(bio_jobs[type]) == 0) {
            // pthread_cond_wait函数会释放之前获取到的锁,
            // 在其它线程执行pthread_cond_signal()函数唤醒时,
            // pthread_cond_wait()函数会重新获取锁。
            pthread_cond_wait(&bio_newjob_cond[type], &bio_mutex[type]);
            continue;
        }
        // 取出链表的首个任务
        ln = listFirst(bio_jobs[type]);
        job = ln->value;
        // 释放所
        pthread_mutex_unlock(&bio_mutex[type]);
        // 关闭文件异步线程
        if (type == BIO_CLOSE_FILE) {
            close((long)job->arg1);
        // aof文件同步执行fsync()函数线程
        } else if (type == BIO_AOF_FSYNC) {
            aof_fsync((long)job->arg1);
        // 异步删除释放资源线程
        } else if (type == BIO_LAZY_FREE) {
            if (job->arg1)
                // 释放异步删除key的
                lazyfreeFreeObjectFromBioThread(job->arg1);
            else if (job->arg2 && job->arg3)
                // 删除数据库释放空间(flushdb和flushall命令)
                lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);
            else if (job->arg3)
                // 集群模式下,删除key时释放slot分片的对应关系
                lazyfreeFreeSlotsMapFromBioThread(job->arg3);
        }
        zfree(job);
        // 加锁
        pthread_mutex_lock(&bio_mutex[type]);
        // 从任务链表里删除任务
        listDelNode(bio_jobs[type], ln);
        // 减少待消费的任务数
        bio_pending[type]--;
    }
}

总结

BIO对于redis来说非常重要,因为一些操作可以交给异步线程来执行,减少了主线程的阻塞时间。