前言
在前文《文件io操作的一些最佳實(shí)踐》中,我介紹了一些 java 中常見(jiàn)的文件操作的接口,并且就 pagecache 和 direct io 進(jìn)行了探討,最近我自己封裝了一個(gè) direct io 的庫(kù),趁著這個(gè)機(jī)會(huì),本文重點(diǎn)談?wù)?java 中 direct io 的意義,以及簡(jiǎn)單介紹下我自己的輪子。
java 中的 direct io
如果你閱讀過(guò)我之前的文章,應(yīng)該已經(jīng)了解 java 中常用的文件操作接口為:filechannel,并且沒(méi)有直接操作 direct io 的接口。這也就意味著 java 無(wú)法繞開(kāi) pagecache 直接對(duì)存儲(chǔ)設(shè)備進(jìn)行讀寫(xiě),但對(duì)于使用 java 語(yǔ)言來(lái)編寫(xiě)的數(shù)據(jù)庫(kù),消息隊(duì)列等產(chǎn)品而言,的確存在繞開(kāi) pagecache 的需求:
- pagecache 屬于操作系統(tǒng)層面的概念,用戶(hù)層面很難干預(yù),user buffercache 顯然比 kernel pagecache 要可控
- 現(xiàn)代操作系統(tǒng)會(huì)使用盡可能多的空閑內(nèi)存來(lái)充當(dāng) pagecache,當(dāng)操作系統(tǒng)回收 pagecache 內(nèi)存的速度低于應(yīng)用寫(xiě)緩存的速度時(shí),會(huì)影響磁盤(pán)寫(xiě)入的速率,直接表現(xiàn)為寫(xiě)入 rt 增大,這被稱(chēng)之為“毛刺現(xiàn)象”
pagecache 可能會(huì)好心辦壞事,采用 direct io + 自定義內(nèi)存管理機(jī)制會(huì)使得產(chǎn)品更加的可控,高性能。
direct io 的限制
在 java 中使用 direct io 最終需要調(diào)用到 c 語(yǔ)言的 pwrite 接口,并設(shè)置 o_direct flag,使用 o_direct 存在不少限制
- 操作系統(tǒng)限制:linux 操作系統(tǒng)在 2.4.10 及以后的版本中支持 o_direct flag,老版本會(huì)忽略該 flag;mac os 也有類(lèi)似于 o_direct 的機(jī)制
- 用于傳遞數(shù)據(jù)的緩沖區(qū),其內(nèi)存邊界必須對(duì)齊為 blocksize 的整數(shù)倍
- 用于傳遞數(shù)據(jù)的緩沖區(qū),其傳遞數(shù)據(jù)的大小必須是 blocksize 的整數(shù)倍。
- 數(shù)據(jù)傳輸?shù)拈_(kāi)始點(diǎn),即文件和設(shè)備的偏移量,必須是 blocksize 的整數(shù)倍
查看系統(tǒng) 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
項(xiàng)目地址
https://github.com/lexburner/kdio
引入依賴(lài)
1
2
3
4
5
|
<dependency> <groupid>moe.cnkirito.kdio</groupid> <artifactid>kdio-core</artifactid> <version> 1.0 . 0 </version> </dependency> |
注意事項(xiàng)
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 寫(xiě)
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 提供工具類(lèi)方法,比如分配 block 對(duì)齊的 bytebuffer
- directchannel/directchannelimpl.java 提供對(duì) fd 的 direct 包裝,提供類(lèi)似 filechannel 的讀寫(xiě) api。
- directrandomaccessfile.java 通過(guò) dio 的方式打開(kāi)文件,并暴露 io 接口。
總結(jié)
這個(gè)簡(jiǎn)單的 direct io 框架參考了smacke/jaydio,這個(gè)庫(kù)自己搞了一套 buffer 接口跟 jdk 的類(lèi)庫(kù)不兼容,且讀寫(xiě)實(shí)現(xiàn)里面加了一塊 buffer 用于緩存內(nèi)容至 block 對(duì)齊有點(diǎn)破壞 direct io 的語(yǔ)義。同時(shí),感謝塵央同學(xué)的指導(dǎo),這個(gè)小輪子的代碼量并不多,初始代碼引用自他的一個(gè)小 demo(已獲得本人授權(quán))。為什么需要這么一個(gè)庫(kù)?主要是考慮后續(xù)會(huì)出現(xiàn)像「中間件性能挑戰(zhàn)賽」和「polardb性能挑戰(zhàn)賽」這樣的比賽,java 本身的 api 可能不足以發(fā)揮其優(yōu)勢(shì),如果有一個(gè)庫(kù)可以屏蔽掉 java 和 cpp 選手的差距,豈不是美哉?我也將這個(gè)庫(kù)發(fā)到了中央倉(cāng)庫(kù),方便大家在自己的代碼中引用。
后續(xù)會(huì)視需求,會(huì)這個(gè)小小的輪子增加注入 fadvise,mmap 等系統(tǒng)調(diào)用的映射,也歡迎對(duì)文件操作感興趣的同學(xué)一起參與進(jìn)來(lái),pull request & issue are welcome!
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:https://lexburner.github.io/direct-io/