BIO即background I/O service,后台I/O服务,是redis的aof持久化后台服务。
redis把阻塞的同步I/O操做交给后台I/O服务来完成:close和fsync。
close加入BIO的缘由
1.若是fd是特定文件描述符的最后一份拷贝,那么文件描述符相关的资源会被释放。
2.若是fd是最后一个引用文件描述符的,而且文件描述符以前已经使用unlink进行删除,那么文件会被删除。
资源释放和文件删除是很是慢的,会阻塞服务器
fsync加入BIO的缘由
把内存中修改的文件数据同步到磁盘。调用者将被阻塞至磁盘报告同步完成。
BIO的设计
目前有两种任务:fsync和close。每种任务一个队列和一个线程。
// 存放工做的队列
static list *bio_jobs[REDIS_BIO_NUM_OPS];
// 记录每种类型 job 队列里有多少 job 等待执行
static unsigned long long bio_pending[REDIS_BIO_NUM_OPS];
//初始化后台服务,启动后台线程
//建立后台任务
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3)
//处理后台任务,后台线程启动函数
void *bioProcessBackgroundJobs(void *arg)
{
//当任务数为0的时候,调用pthread_cond_wait等待通知
//任务数不为0的时候,从队列取任务并处理
// 执行任务,type即线程启动的入参,每一个后台线程处理一种任务
if (type == REDIS_BIO_CLOSE_FILE) {
close((long)job->arg1);
} else if (type == REDIS_BIO_AOF_FSYNC) {
aof_fsync((long)job->arg1);
} else {
redisPanic("Wrong job type in bioProcessBackgroundJobs().");
}
}
BIO的结构
BIO技术应用和优化
aof有三种持久化方案
1。内核同步,交给内核去缓存的数据到磁盘,大约30s一次。
2.每秒同步,这是做者推荐的同步方案,和内核同步几乎同样快。
3.每次都同步,性能极差,做者都巴不得删掉这个功能。
fsync放在单独线程处理存在的问题
fsync线程的使用和主线程的write存在冲突,在fsync进行的时候,write将被阻塞,fsync期间write不能执行,直到fsync完成。
redis中实现了一种优化
部分代码
void flushAppendOnlyFile(int force) {
// 策略为每秒 FSYNC
if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
// 是否有 SYNC 正在后台进行?
sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;
// 每秒 fsync ,而且强制写入为假
if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {
* 若是后台仍在执行 FSYNC ,那么咱们能够延迟写操做一两秒
* (若是强制执行 write 的话,服务器主线程将阻塞在 write 上面)
*/
if (sync_in_progress) {
// 有 fsync 正在后台进行 。。。
* 若是后台还有 fsync 在执行,而且 write 已经推迟 >= 2 秒
* 那么执行写操做(write 将被阻塞)
*/
}
//write写入缓冲区
nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));
优化方案
当aof同步方式为每秒同步时,主线程的write进行优化,若是执行write时fsync服务正在同步中,那么推迟write的时间,最多推迟两秒,若是等待两秒后fsync仍在同步,执行强制write操做,主线程将阻塞等待write完成。
fsync服务线程和bgsave以及bgrewriteaof子进程的冲突
这时候不该该执行fsync
if (server.aof_no_fsync_on_rewrite &&
(server.aof_child_pid != -1 || server.rdb_child_pid != -1))
return;
做者关于fsync服务线程的博客