REDIS fdatasync技术问题和BIO技术的引入
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怎么做的:
首先判断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技术的引入的更多相关文章
- 揭秘丨7分钟看懂华为云鲲鹏Redis背后的自研技术【华为云技术分享】
2019年5月,华为云发布全球首个基于自研ARM架构的分布式缓存鲲鹏Redis,搭载华为LibOS+华为编译器+安全容器引擎三项黑科技,在保证Redis强劲高性能外,还降低客户30%的使用成本,真正实 ...
- Redis03——Redis之单线程+多路IO复用技术
Redis 是单线程+多路IO复用技术 多路复用:使用一个线程来检查多个文件描述符的就绪状态 如果有一个文件描述符就绪,则返回 否则阻塞直到超时 得到就绪状态后进行真正的操作可以在同一个线程里执行,也 ...
- 解决Eclipse中文乱码 - 技术博客 - 51CTO技术博客 http://hsj69106.blog.51cto.com/1017401/595598/
解决Eclipse中文乱码 - 技术博客 - 51CTO技术博客 http://hsj69106.blog.51cto.com/1017401/595598/
- 从ThoughtWorks 2017技术雷达看微软技术
ThoughtWorks在每年都会出品两期技术雷达,这是一份关于技术趋势的报告,它比起一些我们能在市面上见到的其他各种技术行情和预测报告,更加具体,更具可操作性,因为它不仅涉及到新技术大趋势,比如云平 ...
- 广州.NET微软技术俱乐部与其他技术群的区别
.NET和微软技术相关的微信群有不少, 那么广州.NET微软技术俱乐部与其他技术群的区别是什么呢? 有十分大的区别! 本群公告写得很清楚! 本群坚决禁止讨论“JAVA和.NET哪个好”.“NET有没有 ...
- 【渗透技术】渗透测试技术分析_TomCat
[渗透技术]渗透测试技术分析_TomCat 本文转自:i春秋论坛 渗透测试-中间人攻击(原理)说起“中间人攻击”我想大多数对渗透测试又了解的朋友都多少有所了解,因为我们用到的次数真是非常的多.它可以将 ...
- 20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范
20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范 实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法. · 简单应用SET工具建立冒名网站 · ett ...
- 多IDC GSLB的部署 - ADC技术博客 - 51CTO技术博客
多IDC GSLB的部署 - ADC技术博客 - 51CTO技术博客 A10
- 大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app
大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app ( 本文内容为melodyWxy原作,git地址:https://github.com/melodyWx ...
随机推荐
- Jquery做全选
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- JAVA动手动脑异常处理
1>请阅读并运行AboutException.java示例,然后通过后面的几页PPT了解Java中实现异常处理的基础知识. import javax.swing.*; class AboutEx ...
- ASP.NET MVC自定义ActionResult实现文件压缩
有时候需要将单个或多个文件进行压缩打包后在进行下载,这里我自定义了一个ActionResult,方便进行文件下载 using System; using System.Collections; usi ...
- ios 重签名
http://stackoverflow.com/questions/6569120/resigning-an-application-outside-xcode #!/bin/sh TEMPDIR= ...
- DeepLearning之路 (五) CNN
自今年七月份以来,一直在实验室负责卷积神经网络(Convolutional Neural Network,CNN),期间配置和使用过theano和cuda-convnet.cuda-convnet2. ...
- C# 进程和线程
一.进程和线程 进程是对一段静态指令序列的动态执行过程,是系统进行资源分配和调度的基本单位.与进程相关的信息包括进程的用户标志.正在执行的已经编译好的程序.程序和数据在存储器中的位置等.同一个进程有可 ...
- 修改Oracle监听端口
修改oracle监听端口 修改端口号的整体步骤:1.1 查看当前监听的状态1.2 停止监听1.3 修改监听文件的端口号1.4 修改初始化参数local_listener1.5 重启 ...
- Raspberry Pi I2C驱动 (Python)
本文参考 http://www.instructables.com/id/Raspberry-Pi-I2C-Python/all/?lang=zh 作者 AntMan232 In this instr ...
- android 指示器 tablatyout
<android.support.design.widget.TabLayout/>android 材料设计中新出的控件 package com.weavey.loadinglayout; ...
- javap查看class文件
通过JVM编译java文件生成class字节码文件,很多时候很想用工具打开看看,目前还不清楚哪一个软件专门查看class文件的,但是通过windows下的javap命令可以查看详细的class文件 S ...