http://oldblog.antirez.com/post/fsync-different-thread-useless.html

这是原文作者的博客 把他翻译下 带上自己的一些理解 看看作者引入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) != ;

首先判断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 == ) {
/* 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 < ) {
/* 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)) < ) {
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 != - || server.rdb_child_pid != -))
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文件系统部分的内核操作要好好研究下。

REDIS fdatasync技术问题和BIO技术的引入的更多相关文章

  1. 揭秘丨7分钟看懂华为云鲲鹏Redis背后的自研技术【华为云技术分享】

    2019年5月,华为云发布全球首个基于自研ARM架构的分布式缓存鲲鹏Redis,搭载华为LibOS+华为编译器+安全容器引擎三项黑科技,在保证Redis强劲高性能外,还降低客户30%的使用成本,真正实 ...

  2. Redis03——Redis之单线程+多路IO复用技术

    Redis 是单线程+多路IO复用技术 多路复用:使用一个线程来检查多个文件描述符的就绪状态 如果有一个文件描述符就绪,则返回 否则阻塞直到超时 得到就绪状态后进行真正的操作可以在同一个线程里执行,也 ...

  3. 解决Eclipse中文乱码 - 技术博客 - 51CTO技术博客 http://hsj69106.blog.51cto.com/1017401/595598/

    解决Eclipse中文乱码 - 技术博客 - 51CTO技术博客  http://hsj69106.blog.51cto.com/1017401/595598/

  4. 从ThoughtWorks 2017技术雷达看微软技术

    ThoughtWorks在每年都会出品两期技术雷达,这是一份关于技术趋势的报告,它比起一些我们能在市面上见到的其他各种技术行情和预测报告,更加具体,更具可操作性,因为它不仅涉及到新技术大趋势,比如云平 ...

  5. 广州.NET微软技术俱乐部与其他技术群的区别

    .NET和微软技术相关的微信群有不少, 那么广州.NET微软技术俱乐部与其他技术群的区别是什么呢? 有十分大的区别! 本群公告写得很清楚! 本群坚决禁止讨论“JAVA和.NET哪个好”.“NET有没有 ...

  6. 【渗透技术】渗透测试技术分析_TomCat

    [渗透技术]渗透测试技术分析_TomCat 本文转自:i春秋论坛 渗透测试-中间人攻击(原理)说起“中间人攻击”我想大多数对渗透测试又了解的朋友都多少有所了解,因为我们用到的次数真是非常的多.它可以将 ...

  7. 20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范

    20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范 实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法. · 简单应用SET工具建立冒名网站 · ett ...

  8. 多IDC GSLB的部署 - ADC技术博客 - 51CTO技术博客

    多IDC GSLB的部署 - ADC技术博客 - 51CTO技术博客 A10

  9. 大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app

    大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app ( 本文内容为melodyWxy原作,git地址:https://github.com/melodyWx ...

随机推荐

  1. 项目实际部署记录(ubuntu)

    服务器为Ubuntu系统 安装JDK版本:jdk-7u79-linux-x64.tar.gz 安装tomcat版本:apache-tomcat-7.0.69.tar.gz 数据库oracle ,已导出 ...

  2. 使用R进行地图相关的可视化

    Here is a solution using the geosphere and maps package. Using the gcIntermediate function you can & ...

  3. canvas学习笔记:canvas对图片的像素级处理--ImageData的应用

    学习了canvas的基本绘图功能后,惊喜的发现canvas对图片数据也有相当强大的处理功能,能够从像素级别操作位图,当然[lte ie8]不支持. 主要的函数有三个: ctx.createImageD ...

  4. Scala 学习笔记(五)

    def main(args : Array[String]): Unit = { def add(x:Int,y:Int):Int = { return x+y; } def subtract:(In ...

  5. 【 2013 Multi-University Training Contest 6 】

    HDU 4655 Cut Pieces 假设n个数构成的总数都分成了n段,总数是n*a1*a2*...*an.但是答案显然不会那么多. 对于相邻的两个ai,ai+1,如果选择相同的颜色,那么就减少了a ...

  6. 自制c#简易计算器

    这是一个课堂作业,我觉得作为一个简易的计算器不需要态度复杂的东西,可能还有一些bug,有空再慢慢加强. using System;using System.Collections.Generic;us ...

  7. HTML 之 head

    使用 base 标签使页面中的所有标签在新窗口中打开.<!DOCTYPE html> <html> <head> <meta http-equiv=" ...

  8. Primitive JS completion of AJAX

    Firstly , let us explain XMLHttpRequest open(), send(), readyState 1. open(method, url, async, user, ...

  9. CSipSimple通话记录分组

    为了便于查看通话记录,通常要对通话记录进行分组.本质上来说这没什么难度,只需要用ContentResolver去读数据库,剩下的就是策略问题.代码在com/csipsimple/ui/calllog/ ...

  10. 在easyui的treeGrid中添加checkbox(jquery)

    jsp界面,也可用在aspx.html等前台界面中: <script type="text/javascript"> function show(checkid){ v ...