关于 fallocate 文件系统预分配 的一些细粒度测试
文章目录
- Rocksdb 中的预分配
- Fallocate in rocksdb 性能测试
- Fallocate 使用 以及 对应配置的行为
- API 使用
- 不同 Mode 的行为
- 分配磁盘空间
- 释放磁盘空间
- 折叠/裁剪 文件内容
- 清零文件 + 扩容文件
Rocksdb 中的预分配
预分配文件存储空间 在存储引擎中用的还是比较频繁的,尤其是 在Appen-only 形态下的文件写入采用预分配还是对性能比较友好的。
主要优势:
- 能够有效减少每次 io 时 文件系统分配磁盘空间 并 更新对应inode 属性时的开销,让文件系统一次性为当前文件分配合理的磁盘空间能够减少这一部分的开销。
- 对于可靠性要求比较高的 WAL/REDO log文件来说,开启预分配能够减少磁盘空间满时没有空间出现的 i/o 异常问题,因为已经提前分配好了大块空间,坑位已经占下了,尽情写入即可。
我们的os 文件系统提供了预分配机制,并为外部用户提供了相关的系统调用来直接使用。
在 5 系版本的内核下,已经支持预分配机制的内核文件系统包括如下几个。
对于Rocksdb 来说预分配能够进一步提升写 WAL 的性能,尤其是在wal 开sync 的情况下通过预分配好的磁盘空间,能够减少每次请求重新分配磁盘块的开销。
Fallocate in rocksdb 性能测试
做一个简单的测试,如下db_bench 配置,fallocate 会处理文件的预分配,如果要对比差异,也就是对比不用fallocate 时每一个请求需要做一次空间分配的情况,则建议打开 wal 的 sync操作。
./db_bench \-benchmarks="fillsync" \-print_malloc_stats=true \-threads=8 \-sync=true \-value_size=128 \-num=1000000 \-db=./db6 \-compression_type=none \-allow_fallocate=false #db_bench没有这个配置,增加了一个
8 threads, with_fallocate=false
fillsync : 119.996 micros/op 66667 ops/sec; 9.2 MB/s (1000 ops)
8 threads, with_fallocate=true
fillsync : 118.707 micros/op 67391 ops/sec; 9.3 MB/s (1000 ops)
可以看到在开启了预分配之后,整体的性能还是有一部分的提升(10%的样子)
Rocksdb 中对于预分配 接口使用的调用栈为:
WritableFileWriter::AppendFSWritableFile::PrepareWritePosixWritableFile::Allocatefallocate
通过 EnvOptions 的参数allow_fallocate
以及 fallocate_with_keep_size
来控制是否使用fallocate 以及 它的行为。
Fallocate 使用 以及 对应配置的行为
API 使用
接下来主要看一下预分配接口的一些基本使用:
使用的过程是非常简单的,包含头文件#include <linux/falloc.h>
即可,这样fallocate 不同的 mode 名字可以直接使用。
int fallocate(int fd, int mode, off_t offset, off_t len);
- fd 就是通过 open 系统调用返回的文件描述符。
- mode 则是fallocate 支持的不同 配置,用来指定fallocate 的不同行为。
- offset 和 len 表示接下来要操作这个文件的 起始偏移位置 以及 操作的长度。
因为这个接口对于一些用户来说不是很方便,因为它需要使用者清楚多个不同的mode 适用的场景,增加了接口使用的学习成本。很多人只想要使用fallocate 来直接预分配磁盘空间就可以了,其他的不想了解。
所以,glibc 提供了库函数 posix_fallocate:
int posix_fallocate(int fd, off_t offset, off_t len);
用户不需要再关心mode了, 它的底层mode 使用的直接是0,就是直接分配[offset, offset+len]
大小的磁盘空间。
简单代码:
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <linux/falloc.h>using namespace std;
int main(int argc, char* argv[]) {char* filename="111.txt";int fd = open(filename, O_CREAT | O_RDWR, 0660);if (fd == -1) {fprintf(stdout, "open file failed : %d\n", errno);exit(-1);}// 这里也可以替换为 posix_fallocate(fd, 0, 65536);if (fallocate(fd, 0, 0, 65536)) {fprintf(stdout, "fallocate file failed\n");exit(-1);}close(fd);return 0;
}
最后可以看到预分配的文件,文件系统侧看到的大小为64K,占用了128 个磁盘block块(每个 block 为 512B)
$ stat 111.txtFile: ‘111.txt’Size: 65536 Blocks: 128 IO Block: 4096 regular file
Device: 10301h/66305d Inode: 4369192664 Links: 1
Access: (0660/-rw-rw----) Uid: ( 1001/test) Gid: ( 1001/test)
Access: 2021-11-29 00:55:03.484240060 +0800
Modify: 2021-11-29 00:56:42.629443806 +0800
Change: 2021-11-29 00:56:42.629443806 +0800
不同 Mode 的行为
分配磁盘空间
mode=0
,也就是前面测试代码的默认行为,会将inode 的文件大小 以及 需要占用的磁盘空间都直接进行预分配,这种情况下拿到的文件fd 在后续的持续写入时不再需要预分配 以及 修改inode 中的文件大小属性。分配的方式是 直接对设置的 offset 填充 len 个0,当然,如果offset 范围内还有数据,则会跳过这一些数据,直接填充0。
这也是rocksdb 中 建议 写WAL时 进行预分配时采用的mode。
mode=FALLOC_FL_KEEP_SIZE
表示 在预分配的过程中 仅预分配磁盘空间,不会变更文件的inode 元信息。
也就是会出现ls -lh file
和du -sh file
不一致的情况。
测试代码:
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <linux/falloc.h>using namespace std;bool Fallocate(int fd, int use_keep_size, off_t off, off_t len) {int mode = use_keep_size > 0 ? FALLOC_FL_KEEP_SIZE : 0;int ret = fallocate(fd, mode , off, len);if (ret != 0) {fprintf(stdout, "fallocate failed with %d\n", errno);return false;}return true;
}int main(int argc, char* argv[]) {int use_keep_size = 0; // 输入为1 ,则开启keep_size 配置char filename[1024]; // 文件名int use_close = 0; // 关闭文件if (argc > 1) {use_keep_size = atoi(argv[1]);memcpy(filename, argv[2], strlen(argv[2]));use_close = atoi(argv[3]);}int fd = open(filename, O_CREAT | O_RDWR, 0660);if (fd == -1) {fprintf(stdout, "open file failed\n");exit(-1);}size_t size = write(fd, std::string(8192,'a').c_str(), 8192);if (!Fallocate(fd, use_keep_size, 0, 65536)) {exit(-1);}if (use_close) close(fd);return 0;
}
先写入了8192 字节的数据,再预分配 从 0-65536 的空间,看看 使用 mode 为 0
和 FALLOC_FL_KEEP_SIZE
的磁盘空间占用情况:
./test_fallocate 0 nihao1 1
磁盘空间和 file_size 都直接变更为了预分配到的数据$ stat nihao1File: ‘nihao1’Size: 65536 Blocks: 128 IO Block: 4096 regular file Device: 10301h/66305d Inode: 4369192662 Links: 1 Access: (0660/-rw-rw----) Uid: ( 1001/test) Gid: ( 1001/test) Access: 2021-11-29 01:24:50.387540475 +0800 Modify: 2021-11-29 01:24:50.387540475 +0800 Change: 2021-11-29 01:24:50.387540475 +0800$ ls -lh nihao1 -rw-rw---- 1 test test 64K Nov 29 01:24 nihao1$ du -sh nihao1 64K nihao1
./test_fallocate 1 nihao1 1
使用了keep_size 之后 file_size 显示的是真实数据的大小,但是磁盘空间已经分配出来了$ stat nihao1File: ‘nihao1’Size: 8192 Blocks: 128 IO Block: 4096 regular file Device: 10301h/66305d Inode: 4369192662 Links: 1 Access: (0660/-rw-rw----) Uid: ( 1001/test) Gid: ( 1001/test) Access: 2021-11-29 01:25:16.988146849 +0800 Modify: 2021-11-29 01:25:16.988146849 +0800 Change: 2021-11-29 01:25:16.988146849 +0800$ ls -lh nihao1 # -rw-rw---- 1 test test 8.0K Nov 29 01:25 nihao1$ du -sh nihao1 64K nihao1
释放磁盘空间
释放磁盘空间这里主要关注的是一个配置 FALLOC_FL_PUNCH_HOLE
,这个配置需要和 FALLOC_FL_KEEP_SIZE
一起使用。
fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len);
用来标识 从 off 到 off+len 的 fd 文件内容全部写入0,而且对应的文件管理的磁盘block 也会被删除。
根据上面的代码,修改bool Fallocate
函数:
bool Fallocate(int fd, int use_keep_size, off_t off, off_t len) {// use_keep_size :// 0 : 0 预分配磁盘空间 以及 变更文件大小// 1 : FALLOC_FL_KEEP_SIZE 预分配磁盘空间,保留文件大小问为当前数据大小// 2 : FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE 填充0int mode = use_keep_size > 0 ?(use_keep_size == 2 ? FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE :FALLOC_FL_KEEP_SIZE) : 0;int ret = fallocate(fd, mode , off, len);if (ret != 0) {fprintf(stdout, "fallocate failed with %d\n", errno);return false;}return true;
}
我们先在磁盘上写入 8k 的数据,再 对 [0,4k] 的内容打一个洞,预期的文件大小是 8k(因为需要和keep_size 一起使用,keep_size 会保留文件之前的file_size 元信息),但是blocks的个数会从 写入8k 时候的 16个 变为 8个。
$ ./test_fallocate 2 nihao1 1
$ stat nihao1File: ‘nihao1’Size: 8192 Blocks: 8 IO Block: 4096 regular file
Device: 10301h/66305d Inode: 4369192662 Links: 1
Access: (0660/-rw-rw----) Uid: ( 1001/test) Gid: ( 1001/test)
Access: 2021-11-29 23:15:50.102077219 +0800
Modify: 2021-11-29 23:15:50.103077241 +0800
Change: 2021-11-29 23:15:50.103077241 +0800
查看文件内容,可以看到前4K 已经被写入0, 后面的4K都是正常的数据。
需要注意,只有部分文件系统能够支持 FALLOC_FL_PUNCH_HOLE
:xfs, ext4, btrfs, tmpfs, gfs2
折叠/裁剪 文件内容
这里fallocate 通过 FALLOC_FL_COLLAPSE_RANGE
的行为和 ftruncate
系统调用的行为比较相似,就是把制定的 offset – offset+len 这段空间的内容直接折叠并且对应的block也会清理掉,相当于将offset+len 位置的内容覆盖到 offset这个位置。
测试:写入8k 数据,折叠从0 - 4k 位置的内容。
$ stat nihao1File: ‘nihao1’Size: 4096 Blocks: 8 IO Block: 4096 regular file
Device: 10301h/66305d Inode: 4369192662 Links: 1
Access: (0660/-rw-rw----) Uid: ( 1001/web_server) Gid: ( 1001/web_server)
Access: 2021-11-29 23:31:25.588654425 +0800
Modify: 2021-11-29 23:31:25.588654425 +0800
Change: 2021-11-29 23:31:25.588654425 +0800
这个配置目前只在 ext4 和 xfs 上有效。
清零文件 + 扩容文件
这里补充最后两个参数:
FALLOC_FL_ZERO_RANGE
(覆盖原有内容)会将指定的 range 内部所有内容写入0x0,行为有点像是打洞 FALLOC_FL_PUNCH_HOLE ,但是这个会占用磁盘空间以及更新文件大小的meta信息。相当于是覆盖写入了0,后面的读取也会读出0。打洞则不会占用磁盘block空间。这个配置对一个文件重写0 的性能还是很给力的,fallocate 10G 的空间 write 写0 10G的空间,性能差异巨大:
$ time ./test_fallocate 2 nihao1 1 # write 写0 ,写入10G real 0m7.679s user 0m0.608s sys 0m7.047s$ dd if=/dev/zero of=./hello bs=4M count=2560 # dd 写入10G 2560+0 records in 2560+0 records out 10737418240 bytes (11 GB) copied, 4.32988 s, 2.5 GB/s$ time ./test_fallocate 2 nihao1 1 # fallocate 只需要一次系统调用的时间 real 0m0.002s user 0m0.002s sys 0m0.000s
FALLOC_FL_INSERT_RANGE
用于文件扩容,在指定的range 内填充0,不会覆盖原有内容。比如,先写入了8k,后面想要在0 位置插入16k 的空洞, 则8k 的原本内容会被移动到0到16K 后面。并且,这个配置会分配磁盘块空间 以及 更新对应的文件元数据。感觉这个配置有点鸡肋,还不如直接 ftruncate。而且,这个配置仅在 4.1 及 以后版本支持xfs,4.2 及以后版本支持 ext4。
相关文章:

mac 使用nvm安装node
1.curl https://raw.github.com/creationix/nvm/master/install.sh | sh2。vi ~/.bash_profile 添加:source /Users/dujie/.nvm/nvm.sh nvm install 0.10.24 nvm use 0.10.24 # 默認使用 0.10.24 版本,否則每次關掉 Terminal 就得重新 nvm use 一次 $…

Java项目:人事管理系统(java+javaweb+jdbc)
源码获取:博客首页 "资源" 里下载! 功能介绍: 登录、新增、修改、离职 员工管理控制层: Controller RequestMapping("/employee") public class EmployeeController {Autowiredprivate IEmployeeService em…

转:async await 的前世今生 ; 异步 线程 多线程
写的非常好,改天搬过来
ubuntu14.04初体会
2014年4月17日ubuntu新的长期支持版14.04公布了,中国时间18日一早就能够下载到。18日晚。在我的X200上安装上了14.04,算是比較早一批体会到14.04正式版的人吧。对照12.04,14.04提升的执行速度非常明显,界面改善也是令人眼前一亮&a…

Linux 下获取本机所有网卡 以及 网卡对应ip 列表
简单record 一下 #include <arpa/inet.h> // struct sockaddr_in #include <errno.h> #include <net/if.h> // struct ifreq and struct if_nameindex #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/i…

Java项目:植物大战僵尸(java+swing)
源码获取:博客首页 "资源" 里下载! 功能简介: 植物大战僵尸、冒险模式、生存模式、解谜模式 小车服务类: public class CarThread extends Thread{private boolean flagtrue;private int x;private int y;private JL…

秋实大哥の恋爱物语
//裸kmp,劳资居然不会写!!!!!! 题意:中文题面自己看 解:差分裸kmp 因为可以上下移动,所以只要变化趋势相符就行,于是我们先做一个差分,…

《马哥出品高薪linux运维教程》wingkeung学习笔记-linux基础入门课程5
命令:内部命令:由shell程序自带的命令叫做内部命令;外部命令:在系统的某个路径下,有一个与命令同名的可执行程序叫做外部命令。查看内外部命令的命令:type 命令命令选项:用于调整命令执行行为的…

八、LaTex中的表格
转载于:https://www.cnblogs.com/invisible2/p/10813964.html

基于持久内存的 单机上亿(128B)QPS -- 持久化 k/v 存储引擎
文章目录性能数据设计背景设计架构Hash 索引结构 及 PMEM空间管理形态基本API 及 实现API初始化流程写流程读流程删除流程PMEM Allocator设计主要组件空间分配流程空间释放图数据库 on KVDK 性能性能数据 这个kv 存储引擎是持久化的存储引擎,存储介质是PMEM&#x…

SCALA当的trait
不是特别懂,但感觉和RUBY当中的MIX-IN功能有几分相似,这又扯到了多重继承及JAVA当中的接口虚拟类了。。 package com.hengheng.scalaclass UseTrait {} trait Logger {def log(msg : String) {println("log : " msg)} } trait ConsoleLogger …

Java项目:贪吃蛇游戏(java+swing)
源码获取:博客首页 "资源" 里下载! 功能简介: 贪吃蛇游戏 大嘴鱼洁面类。完成大嘴鱼的界面的绘制: /*** 大嘴鱼洁面类。完成大嘴鱼的界面的绘制。*/ public class BigMouthFishFrame extends JFrame{private FishPool pool null;…

使用Ext Form自动绑定Html中的Form元素
2019独角兽企业重金招聘Python工程师标准>>> Java代码 //把ext 对象绑定在Html Form元素时的ext属性中 Ext.override(Ext.Component, { initComponent :function(){ this.on(render, function(){ if(this.el) Ext.getDom(this.el).ext this; …

Directx11 教程(2) 基本的windows应用程序框架(2)
Directx11 教程(2) 基本的windows应用程序框架(2) 原文:Directx11 教程(2) 基本的windows应用程序框架(2)在本教程中,我们把前面一个教程的代码,进行封装。把初始化函数,Run函数,窗口回调函数,ShutdownWindows函数等封…

Rocksdb的事务(二):完整事务体系的 详细实现
文章目录1. 基本事务操作1.1 TransactionDB -- Pessimistic1.2 OptimisticTransactionDB1.3 Read Uncommitted1.4 SavePoint 回滚部分事务操作1.5 SetSnapshot1.6 GetForUpdate1.7 RepeatableRead2. 实现2.1 WBWI(write batch with index) & WB(write batch)2.2 Pessimisti…

关于学习编程的一些看法
1、看书,书上的代码一串一串的对吧?是不是很不好记?是不是觉得如果自己把这些代码都敲一遍很浪费时间?其实对于一些完全没有任何基础的人来说,全部敲一遍不失为一种简单的入门方法。对于有一点基础的人来说,…

Java项目:日历万年历(java+swing)
源码获取:博客首页 "资源" 里下载! 功能简介: 万年历 启动类: public class CalendarMainClass { public static void main(String args[]) { try { UIManager.setLookAndFeel("com.sun.java.swing.pl…

求大神给解释一下H3C ospf 双塔奇兵
转载于:https://blog.51cto.com/2807200/1364566

活着是为了什么?
活着是为了死亡,死亡才是完美,才是永恒。 死亡时将一无所有,所以活着不是为了能带走什么,而应该是能留下什么,这才是人活着的意义,多少人能想明白呢? 胡建龙转载于:https://www.cnblogs.com/hjl…

XFS 文件系统 (一) :设计概览
文章目录0 前言1 设计背景2. 需要解决的问题2.1 异常恢复太慢2.2 不支持大文件系统2.3 不支持大型稀疏文件2.4 不支持大型连续文件2.5 不支持大目录2.6 不支持过多文件个数3 XFS 架构4 痛点解决4.1 Allocation Groups4.2 Manging Free Space4.3 大文件的支持5 总结0 前言 虽然…

WebApi2官网学习记录---异常处理
HttpResponseException 当WebAPI的控制器抛出一个未捕获的异常时,默认情况下,大多数异常被转为status code为500的http response即服务端错误。 HttpResonseException是一个特别的情况,这个异常可以返回任意指定的http status code࿰…

Java项目:资源下载工具(java+swing)
源码获取:博客首页 "资源" 里下载! 功能简介: 下载地址、保存位置、下载设置、下载进度 文件仓库控制器: /*** ClassName: FileStoreController* Description: 文件仓库控制器**/ Controller public class FileStoreC…

江南Style之---西湖
西湖古称“钱塘湖”,又名“西子湖”,古代诗人苏轼就对它评价道:“欲把西湖比西子,淡妆浓抹总相宜。西湖,是一首诗,一幅天然图画,一个美丽动人的故事,不论是多年居住在这里的人还是匆…

mimikatz
下载后,在目标机直接运行 常用命令: 提升权限:privilege::debug 获取用户登录明文账号密码:sekurlsa::logonPasswords 获取用户密码hash值:lsadump::sam 转载于:https://www.cnblogs.com/xiaoqiyue/p/10824169.html

通过 RDTSC 指令从 CPU 寄存器中直接获取系统时钟
很多时候我们使用函数 gettimeofday 以及 clock_gettime 作为我们获取 wall lock的时钟函数。 因为这两种函数是 glibc 提供的用户封装,简单易用,而且能够精确到 ns,对于大多数的时钟需求场景都已经够用了。 但是如果 我们的应用 调用时钟频…

Java项目:星际争霸游戏(java+swing+awt界面编程+IO输入输出流+socket+udp网络通信)
源码获取:博客首页 "资源" 里下载! 功能简介: 星际争霸游戏项目,该项目实现了单人模式和多人合作模式,可记录游戏进度,新建游戏,载入历史记录等功能,多人模式下可以创建一…

GTONE清理维护建议方案
1、日志清理/home/gtone/AppGov/analyzer/log//home/gtone/AppGov/analyzer/SRC/temp//home/gtone/AppGov/WAS/logs/ 2、扩容现有磁盘空间至200GB转载于:https://www.cnblogs.com/arcer/p/4461018.html

[C#]委托和事件(讲解的非常不错)
引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉…

BZOJ1460: Pku2114 Boatherds
题目链接:点这里 题目描述:给你一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 数据范围:\(n\le1e5\,,p\le100\,,长度\le1e5\) Solution: …

centos 自定义内核模块 编译运行
简单记录一下 centos 自定义内核模块的一些编译运行记录,代码如下: 主要功能是通过rdtsc 指令直接从 CPU MSR 寄存器中获取时钟,尝试获取两次,两次之间会做一些赋值操作什么的,并记录一下时差。 #include <linux/…