博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
REDIS fdatasync技术问题和BIO技术的引入
阅读量:5111 次
发布时间:2019-06-13

本文共 4569 字,大约阅读时间需要 15 分钟。

这是原文作者的博客 把他翻译下 带上自己的一些理解 看看作者引入fdatasync和bio技术上的一些探讨:【非常有用的磁盘持久化技术

对于从kernel buffers写入到磁盘中这点是非常重要的,fdatasync就是做这个工作,当然本身磁盘本省也有cache层 这层对于我们来讲可以暂时不用考虑,

对于fsync() 把文件数据和文件元信息写入强刷到磁盘中,速度是比较慢的。

REDIS提供一个持久化的模式叫做Append Only File.数据集的每个改变都会被写入到磁盘中,就是一个操作日志型的操作。所以fsync()操作是必须得用的【如果想要安全的话 这是必须的操作】

因为fsync是慢的 REDIS就允许有3种不同的策略:

fsync never: 让kernel后台线程去做  这个线程可能是30秒去做一次哦

fsync everysec: 一秒调用fsync

fsync always:每次有write操作到AOF里 就会调用fsync

fsync erverysec 是一个很好的这种折中对于性能和安全这2个点。我们仅仅涉及一个能够每秒做一次sync但是不会阻塞我们反馈给客户端信息的操作。所以作者觉得这样做必须把fsync call调用就移到了另外一个线程里。用这种方式来处理,也许fsync会引起DISK非常繁忙 但是没有用户会注意到这种延迟。因为客户端所需要的数据还是在内存里存在,redis还是照样能够很好的工作。

貌似正确 但是我开始感觉这是无用的  因为write调用会被阻塞如果一个fsync()去处理同一个文件的时候。

 

现在我们尝试的来分析下【非翻译部分】

目前而言 前面探讨了这个fdatasync技术 现在我们来看看BIO技术和怎么解决和提高AOF的技术。

BIO: backgroud IO .首先fsync是必须要调用的 不过他多慢  其次fsync必须要放在其他thread来做 也就是backgroud thread.

background thread 目前redis主要做2个事情:

1 fdatasync(fd)

2 close(fd)

线程1 来做fdatasync(fd)  线程2做close(fd)

#define REDIS_BIO_CLOSE_FILE    0 /* Deferred close(2) syscall. */#define REDIS_BIO_AOF_FSYNC     1 /* Deferred AOF fsync. */

在bio.c:

static pthread_mutex_t bio_mutex[REDIS_BIO_NUM_OPS];static pthread_cond_t bio_condvar[REDIS_BIO_NUM_OPS];static list *bio_jobs[REDIS_BIO_NUM_OPS];

在aof.c文件里 会调用bioCreateBackgroundJob(),来创建2个JOB 放在thread1和thread2之后 线程就完成了在后台处理这2个动作。

可以看下面的图: 就能了解大概的做的一些动作:

这里我就假设你较为熟悉AOF机制,下期我会写下AOF持久化。 这样就暴露出了一个问题:

AOF创建了一个进程之后 flushAppendOnlyFile 要把AOF文件写入到磁盘里,可以试想下:主线程一方面要调用write()把aofbuf写入到内核缓冲区,而另外一个线程调用fdatasync这就比较麻烦了这样会阻止了主线程的一个阻塞动作,影响了客户端的延迟,你懂的,这不是我们所想要的,看看redis怎么做的:

if (server.aof_fsync ==
AOF_FSYNC_EVERYSEC) sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;

首先判断aof_FSYNC那个线程里面有没有任务,如果有就要标记下sync_in_progress

if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {        /* With this append fsync policy we do background fsyncing.         * If the fsync is still in progress we can try to delay         * the write for a couple of seconds. */        if (sync_in_progress) {            if (server.aof_flush_postponed_start == 0) {                /* No previous write postponinig, remember that we are                 * postponing the flush and return. */                server.aof_flush_postponed_start = server.unixtime;                return;            } else if (server.unixtime - server.aof_flush_postponed_start < 2) {                /* We were already waiting for fsync to finish, but for less                 * than two seconds this is still ok. Postpone again. */                return;            }            /* Otherwise fall trough, and go write since we can't wait             * over two seconds. */            server.aof_delayed_fsync++;            redisLog(REDIS_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.");        }

sync_in_progress不等于0 是有fdatasync做这个动作,这里有2个条件让他不会真正的做 而是延迟fdatasync。如果本身start还是0 设置下sart时间 然后退出,或者Unixtime-start时间小于2 也返回。如果fdatasync执行有2秒钟了 如果都不满足 那么就要做真正的fdatasync 注意了 这里就会引起write阻塞导致主服务阻塞。

nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));

这里write可能会导致了主线程阻塞并不是write 因为wirte的aof_buf并不大 基本上不会导致阻塞,而是后台的fdatasync导致write等待datasync完成了之后才调用write导致阻塞 当然这个2延迟其实是可以修改的。看你自己的应用来看。当然改多了也不行 权衡需要自己把握好。

接下来:

server.aof_current_size += nwritten;    /* Re-use AOF buffer when it is small enough. The maximum comes from the     * arena size of 4k minus some overhead (but is otherwise arbitrary). */    if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {        sdsclear(server.aof_buf);    } else {        sdsfree(server.aof_buf);        server.aof_buf = sdsempty();    }    /* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are     * children doing I/O in the background. */    if (server.aof_no_fsync_on_rewrite &&        (server.aof_child_pid != -1 || server.rdb_child_pid != -1))            return;

然后处理了aof缓冲区部分其是说如果小于4k 就clear 如果不是 就free掉 这里保证了一个内存的重复利用性

如果有子进程做aof或者rdb存储工作 就拒绝调用fsync。

这里又做了相关优化: 因为前面调用的是fdatasync 没有修改文件大小元信息到磁盘里 所以呢 如果write成功之后 就调用了ftruncate(server.aof_fd, server.aof_current_size)来修改aof_fd的大小。

if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&                server.unixtime > server.aof_last_fsync)) {        if (!sync_in_progress) aof_background_fsync(server.aof_fd);        server.aof_last_fsync = server.unixtime;    }

如果没有后台fsync 并且unixtime>aof_last_fsync(这个unixtime更新是一秒更新一次的 )则要进行fdatasync操作:aof_last_fsync改成当前时间 然后aof_backgroud_fsync把fsync加入到后台操作里面。由此可以看出来 要写出高性能的文件刷新程序 是要对linux核的文件系统要非常了解 以后有时间要把linux文件系统部分的内核操作要好好研究下。

 

转载于:https://www.cnblogs.com/sfwtoms/p/3947238.html

你可能感兴趣的文章
centos6.5虚拟机配置Nat模式连接外网
查看>>
node 标准输入流和输出流
查看>>
ubuntu 12.04上安装emacs24
查看>>
51Nod 1199 - Money out of Thin Air(树链剖分)
查看>>
“大话架构”阿里架构师分享Java程序员需要突破的技术要点
查看>>
OC 委托delegate 与 Block
查看>>
介绍个图文混排的开源库
查看>>
js文件合并,压缩,缓存,延迟加载
查看>>
[POJ2750]Potted Flower
查看>>
[POI2008]BLO
查看>>
前端--BootStrap
查看>>
django orm 多表操作2(基于对象查,基于双下划线连表查)
查看>>
ZOJ 3861 Valid Pattern Lock
查看>>
第六天,尝试给图片文字加入链接
查看>>
adb常用命令(转)
查看>>
字符编码与大小端
查看>>
mybatis罕见问题记录
查看>>
asp.net mvc Razor一点小注意点
查看>>
“.Net 社区虚拟大会”(dotnetConf) 2016 Day 1 Keynote: Scott Hunter
查看>>
正则表达式类
查看>>