专栏首页Kirito的技术分享Java 文件 IO 操作之 DirectIO

Java 文件 IO 操作之 DirectIO

来源:《中二病也要谈恋爱》凸守早苗

在前文《文件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 接口,并设置 ODIRECT flag,使用 ODIRECT 存在不少限制

  • 操作系统限制:Linux 操作系统在 2.4.10 及以后的版本中支持 ODIRECT flag,老版本会忽略该 Flag;Mac OS 也有类似于 ODIRECT 的机制
  • 用于传递数据的缓冲区,其内存边界必须对齐为 blockSize 的整数倍
  • 用于传递数据的缓冲区,其传递数据的大小必须是 blockSize 的整数倍。
  • 数据传输的开始点,即文件和设备的偏移量,必须是 blockSize 的整数倍

查看系统 blockSize 大小的方式 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

引入依赖

<dependency>    <groupId>moe.cnkirito.kdio</groupId>    <artifactId>kdio-core</artifactId>    <version>1.0.0</version></dependency>

注意事项

// file path should be specific since the different file path determine whether your system support direct iopublic 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 4kbprivate static final int BLOCK_SIZE = 4 * 1024;

Direct IO 写

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 读

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

  1. DirectIOLib.java 提供 Native 的 pwrite 和 pread
  2. DirectIOUtils.java 提供工具类方法,比如分配 Block 对齐的 ByteBuffer
  3. DirectChannel/DirectChannelImpl.java 提供对 fd 的 Direct 包装,提供类似 FileChannel 的读写 API。
  4. 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!

本文分享自微信公众号 - Kirito的技术分享(cnkirito)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 文件IO操作的最佳实践

    已经过去的中间件性能挑战赛,和正在进行中的 第一届 PolarDB 数据性能大赛 都涉及到了文件操作,合理地设计架构以及正确地压榨机器的读写性能成了比赛中获取较...

    kirito-moe
  • Java随机数探秘

    一提到 Java 中的随机数,很多人就会想到 Random,当出现生成随机数这样需求时,大多数人都会选择使用 Random 来生成随机数。Random 类是线程...

    kirito-moe
  • 以Dubbo为例,聊聊如何为开源项目做贡献

    Github 上有众多优秀的开源项目,大多数 IT 从业者将其当做了予取予求的工具库,遇到什么需求,先去 Github 搜一把,但有没有想过有一天自己也可以给开...

    kirito-moe
  • 邹良城:视频聚合类APP的网络版权侵权认定问题

      本文为作者在“第三届中国互联网新型版权研讨会——互联网+内容产业的生态发展及制度保障”上的发言。 ? 邹良城  腾讯公司诉讼维权总监   各位领导,...

    腾讯研究院
  • RabbitMQ学习系列教程四:Exchange 交换机及直连交换机简单使用

    Auto Delete:当最后一个绑定到exchange上的队列删除后,自定删除该exchange

    凯哥Java
  • 开源托管站点大全

    landv
  • 论linux下计划任务

    小伙伴们,平时做程序开发的时候,是否也曾为实现一个商城网站或者一个小程序自动执行某个方法而苦恼呢? 今天思梦php就给大家带了这...

    思梦php
  • 习近平访美演讲全文:中国反腐没有权利斗争,没有纸牌屋!

    大数据文摘
  • jQuery基础--事件处理

    简单事件绑定>>bind事件绑定>>delegate事件绑定>>on事件绑定(推荐)

    eadela
  • IDEA 设置类和方法注释模板

    File -> Settings -> Editor -> File and Code Templates

    Remember_Ray

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动