在前文《文件IO操做的一些最佳實踐》中,我介紹了一些 Java 中常見的文件操做的接口,而且就 PageCache 和 DIrect IO 進行了探討,最近我本身封裝了一個 Direct IO 的庫,趁着這個機會,本文重點談談 Java 中 Direct IO 的意義,以及簡單介紹下我本身的輪子。java
若是你閱讀過我以前的文章,應該已經瞭解 Java 中經常使用的文件操做接口爲:FileChannel,而且沒有直接操做 Direct IO 的接口。這也就意味着 Java 沒法繞開 PageCache 直接對存儲設備進行讀寫,但對於使用 Java 語言來編寫的數據庫,消息隊列等產品而言,的確存在繞開 PageCache 的需求:git
PageCache 可能會好心辦壞事,採用 Direct IO + 自定義內存管理機制會使得產品更加的可控,高性能。github
在 Java 中使用 Direct IO 最終須要調用到 c 語言的 pwrite 接口,並設置 O_DIRECT flag,使用 O_DIRECT 存在很多限制數據庫
查看系統 blockSize 大小的方式:stat /boot/|grep "IO Block"ubuntu
ubuntu@VM-30-130-ubuntu:~$ stat /boot/|grep "IO Block" Size: 4096 Blocks: 8 IO Block: 4096 directory緩存
一般爲 4kb微信
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 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;
複製代碼
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");
}
}
複製代碼
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");
}
}
複製代碼
DirectIOLib.java
提供 Native 的 pwrite 和 preadDirectIOUtils.java
提供工具類方法,好比分配 Block 對齊的 ByteBufferDirectChannel/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 選手的差距,豈不是美哉?我也將這個庫發到了中央倉庫,方便你們在本身的代碼中引用。dom
後續會視需求,會這個小小的輪子增長注入 fadvise,mmap 等系統調用的映射,也歡迎對文件操做感興趣的同窗一塊兒參與進來,pull request & issue are welcome!工具
歡迎關注個人微信公衆號:「Kirito的技術分享」,關於文章的任何疑問都會獲得回覆,帶來更多 Java 相關的技術分享。