前言
在前文《文件io操作的一些最佳实践》中,我介绍了一些 java 中常见的文件操作的接口,并且就 pagecache 和 direct io 进行了探讨,最近我自己封装了一个 direct io 的库,趁着这个机会,本文重点谈谈 java 中 direct io 的意义,以及简单介绍下我自己的轮子。
java 中的 direct io
如果你阅读过我之前的文章,应该已经了解 java 中常用的文件操作接口为:filechannel,并且没有直接操作 direct io 的接口。这也就意味着 java 无法绕开 pagecache 直接对存储设备进行读写,但对于使用 java 语言来编写的数据库,消息队列等产品而言,的确存在绕开 pagecache 的需求:
- pagecache 属于操作系统层面的概念,用户层面很难干预,user buffercache 显然比 kernel pagecache 要可控
- 现代操作系统会使用尽可能多的空闲内存来充当 pagecache,当操作系统回收 pagecache 内存的速度低于应用写缓存的速度时,会影响磁盘写入的速率,直接表现为写入 rt 增大,这被称之为“毛刺现象”
pagecache 可能会好心办坏事,采用 direct io + 自定义内存管理机制会使得产品更加的可控,高性能。
direct io 的限制
在 java 中使用 direct io 最终需要调用到 c 语言的 pwrite 接口,并设置 o_direct flag,使用 o_direct 存在不少限制
- 操作系统限制:linux 操作系统在 2.4.10 及以后的版本中支持 o_direct flag,老版本会忽略该 flag;mac os 也有类似于 o_direct 的机制
- 用于传递数据的缓冲区,其内存边界必须对齐为 blocksize 的整数倍
- 用于传递数据的缓冲区,其传递数据的大小必须是 blocksize 的整数倍。
- 数据传输的开始点,即文件和设备的偏移量,必须是 blocksize 的整数倍
查看系统 blocksize 大小的方式:stat /boot/|grep “io block”
ubuntu@vm-30-130-ubuntu:~$ stat /boot/|grep “io block”
size: 4096 blocks: 8 io block: 4096 directory通常为 4kb
java 使用 direct io
项目地址
https://github.com/lexburner/kdio
引入依赖
|
1
2
3
4
5
|
<dependency>
<groupid>moe.cnkirito.kdio</groupid>
<artifactid>kdio-core</artifactid>
<version>1.0.0</version>
</dependency>
|
注意事项
|
1
2
3
4
5
|
// file path should be specific since the different file path determine whether your system support direct io
public static directiolib directiolib = directiolib.getlibforpath("/");
// you should always write into your disk the integer-multiple of block size through direct io.
// in most system, the block size is 4kb
private static final int block_size = 4 * 1024;
|
direct io 写
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private static void write() throws ioexception {
if (directiolib.binit) {
bytebuffer bytebuffer = directioutils.allocatefordirectio(directiolib, 4 * block_size);
for (int i = 0; i < block_size; i++) {
bytebuffer.putint(i);
}
bytebuffer.flip();
directrandomaccessfile directrandomaccessfile = new directrandomaccessfile(new file("./database.data"), "rw");
directrandomaccessfile.write(bytebuffer, 0);
} else {
throw new runtimeexception("your system do not support direct io");
}
}
|
direct io 读
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public static void read() throws ioexception {
if (directiolib.binit) {
bytebuffer bytebuffer = directioutils.allocatefordirectio(directiolib, 4 * block_size);
directrandomaccessfile directrandomaccessfile = new directrandomaccessfile(new file("./database.data"), "rw");
directrandomaccessfile.read(bytebuffer, 0);
bytebuffer.flip();
for (int i = 0; i < block_size; i++) {
system.out.print(bytebuffer.getint() + " ");
}
} else {
throw new runtimeexception("your system do not support direct io");
}
}
|
主要 api
- directiolib.java 提供 native 的 pwrite 和 pread
- directioutils.java 提供工具类方法,比如分配 block 对齐的 bytebuffer
- directchannel/directchannelimpl.java 提供对 fd 的 direct 包装,提供类似 filechannel 的读写 api。
- directrandomaccessfile.java 通过 dio 的方式打开文件,并暴露 io 接口。
总结
这个简单的 direct io 框架参考了smacke/jaydio,这个库自己搞了一套 buffer 接口跟 jdk 的类库不兼容,且读写实现里面加了一块 buffer 用于缓存内容至 block 对齐有点破坏 direct io 的语义。同时,感谢尘央同学的指导,这个小轮子的代码量并不多,初始代码引用自他的一个小 demo(已获得本人授权)。为什么需要这么一个库?主要是考虑后续会出现像「中间件性能挑战赛」和「polardb性能挑战赛」这样的比赛,java 本身的 api 可能不足以发挥其优势,如果有一个库可以屏蔽掉 java 和 cpp 选手的差距,岂不是美哉?我也将这个库发到了中央仓库,方便大家在自己的代码中引用。
后续会视需求,会这个小小的轮子增加注入 fadvise,mmap 等系统调用的映射,也欢迎对文件操作感兴趣的同学一起参与进来,pull request & issue are welcome!
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对快网idc的支持。
原文链接:https://lexburner.github.io/direct-io/
相关文章
- 64M VPS建站:能否支持高流量网站运行? 2025-06-10
- 64M VPS建站:怎样选择合适的域名和SSL证书? 2025-06-10
- 64M VPS建站:怎样优化以提高网站加载速度? 2025-06-10
- 64M VPS建站:是否适合初学者操作和管理? 2025-06-10
- ASP.NET自助建站系统中的用户注册和登录功能定制方法 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-27 62
-
创建个人或企业网站:租用虚拟专用服务器(VPS)还是物理服务器?
2025-06-05 58 -
2025-05-25 69
-
2025-05-25 57
-
2025-05-25 98

