文章目录
- 前言
- 一、异步线程
- 二、代码实现
- 总结
前言
Redis BIO其实就是用于异步操作的,理解这点非常重要。
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来说非常重要,因为一些操作可以交给异步线程来执行,减少了主线程的阻塞时间。