Ubuntu 22服务器小文件写入偶现20ms阻塞延迟问题问询
Ubuntu 22服务器小数据量文件写入偶发20ms延迟问题
问题描述
在Ubuntu 22服务器上观测到,即便是仅写入几字节的极小数据量,文件写入操作也经常出现约20ms的延迟。
从测试结果可总结出两个规律:
- 两次写入操作间隔时间较长、待写入的目标文件已存在时,延迟出现的概率明显升高
- 写入文件前先删除目标文件反而能提升写入性能,和常规认知相反
测试服务器硬件配置为6核Xeon 2386G处理器,双NVMe SSD组建软RAID,测试期间服务器几乎无其他业务运行,无其他登录用户。
复现代码
#include <iostream> #include <fstream> #include <chrono> #include <filesystem> #include <thread> using namespace std; void go_c() { FILE *out = fopen("hello.txt", "w"); fputs("hello", out); fclose(out); } void go_cpp () { ofstream out("hello.txt"); out<<"hello"<<endl; } double test(void (*f)()) { typedef chrono::time_point <chrono::steady_clock> tp; tp t0 = chrono::steady_clock::now(); f(); tp t1 = chrono::steady_clock::now(); return chrono::duration<double>(t1-t0).count() * 1000; // 单位:毫秒 } void bench(void (*f)(), const char *txt, int delay_ms) { filesystem::remove("hello.txt"); for (int i=0;i<5;i++) { double t = test(f); cerr<<i<<": "<<txt<<", time = "<<t<<" ms"<<endl; this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); } cerr<<endl; } int main () { bench(go_c, "C Write", 0); bench(go_cpp, "C++ Write", 0); bench(go_c, "C Write with delay", 2500); bench(go_cpp, "C++ Write with delay", 2500); return 0; }
编译运行命令:
g++ -o write3 write3.cpp -O2 -Wall ./write3
测试输出
0: C Write, time = 0.09978 ms 1: C Write, time = 21.9316 ms 2: C Write, time = 0.185957 ms 3: C Write, time = 0.140212 ms 4: C Write, time = 0.139051 ms 0: C++ Write, time = 0.145766 ms 1: C++ Write, time = 0.091845 ms 2: C++ Write, time = 0.139618 ms 3: C++ Write, time = 0.130834 ms 4: C++ Write, time = 0.132217 ms 0: C Write with delay, time = 0.048674 ms 1: C Write with delay, time = 0.23875 ms 2: C Write with delay, time = 20.8626 ms 3: C Write with delay, time = 8.4307 ms 4: C Write with delay, time = 19.4026 ms 0: C++ Write with delay, time = 17.1555 ms 1: C++ Write with delay, time = 17.5887 ms 2: C++ Write with delay, time = 18.9792 ms 3: C++ Write with delay, time = 25.8653 ms 4: C++ Write with delay, time = 20.7998 ms
环境信息
执行uname -a与dpkg --list | grep -E "libc6?-(dev|bin)"命令的系统环境截图:
根因说明
这个稳定在20ms左右的延迟不是硬件故障,是Ubuntu 22.04默认内核配置、文件系统逻辑和软RAID机制共同作用的结果:
- 测试代码中使用
"w"模式打开文件属于截断写入,当目标文件已存在时,fclose阶段会触发文件元数据(修改时间、文件大小、块映射)的日志提交操作,该操作默认同步阻塞,需要等待存储端确认落盘才会返回。 - 内核默认脏页过期时间为3000厘秒(即3秒),当两次写入间隔超过这个时间,之前写入的文件页缓存会被标记为待回收,覆盖写入时需要先做旧页无效化,同时触发文件系统日志即时提交,不会等待默认的5秒批量提交窗口。软RAID1场景下这个等待时间会被放大,需要等两块NVMe都完成落盘确认才会返回,调度开销加双盘确认等待的总时长刚好落在15~25ms区间,和测试得到的延迟值完全匹配。
- 写入前先删除文件的场景属于新建文件而非截断旧文件,走元数据快速追加路径,不需要等待旧文件元数据的刷盘确认,因此速度更快,这就是“删文件反而提升性能”这一反直觉现象的成因。
- 无间隔连续写入时,多次写入的元数据更新会合并到同一次日志批量提交中,因此大部分时候延迟都在0.1ms级别,只有刚好撞上日志提交窗口的那次操作会出现高延迟,和第一轮C写入的测试结果吻合。
内容的提问来源于stack exchange,提问作者CaptainCodeman
相关产品推荐
相关产品推荐

