前言
本文参考源码版本为 redis6.2
我们常说 redis 是单线程模型,一般是指正常的 请求处理+周期任务。其中:
- 处理请求包括:包括接收连接、IO监听/读/写以及命令执行。
- 周期任务,如删除过期key、字典 rehash 等。
其实,还有一些非常耗时的操作,redis 通过专用的线程来处理,这里的专用线程,便是我们这篇文章的主角,我们接着往下看~
一、还有谁?
截止到目前,redis 共有三个后台线程,分别是 close_file、aof_fsync和lazy_free:
- close_file 表示关闭相应文件描述符对应的文件(释放套接字、数据空间等)。
- aof_fsync 表示 AOF 刷盘
- lazy_free 表示惰性释放空间
截止 redis6.0 之前,关于 redis 的线程模型,我画了张图,你可以看下:
为了方便,我们将处理用户连接相关的线程称为主线程,反之为后台线程。
换句话说,目前 redis 中存在两种线程(先不谈 redis 6.0 出现的 IO 线程);即,主线程和后台线程。
由于之前系列文章已经介绍了很多主线程相关东西(如果不清楚的话,可以找来看看),本文将主要介绍后台线程。
二、后台线程
1. 初始化
在 server.c#main 函数启动最后阶段,调用了方法 InitServerLast:
// server.c#InitServerLast
void InitServerLast() {
bioInit();
initThreadedIO();
set_jemalloc_bg_thread(server.jemalloc_bg_thread);
server.initial_memory_usage = zmalloc_used_memory();
}
其中,bioInit() 则是后台线程初始化,bio 全名叫 Background I/O ,即后台IO:
void bioInit(void) {
pthread_attr_t attr;
pthread_t thread;
size_t stacksize;
int j;
// 变量初始化
for (j = 0; j < BIO_NUM_OPS; j++) {
pthread_mutex_init(&bio_mutex[j],NULL);
pthread_cond_init(&bio_newjob_cond[j],NULL);
pthread_cond_init(&bio_step_cond[j],NULL);
bio_jobs[j] = listCreate();
bio_pending[j] = 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);
// 创建线程,下标 0,1,2 代表不同的线程
for (j = 0; j < BIO_NUM_OPS; j++) {
void *arg = (void*)(unsigned long) j;
if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {
ser