回憶一下
我們都知道Linux的IO模型有阻塞、非阻塞、SIGIO、多路復(fù)用(select,epoll)、AIO(異步I/O)等。
數(shù)據(jù)庫可能比較傾向于使用AIO。從時(shí)序上面來講,AIO是用戶應(yīng)用發(fā)起IO請(qǐng)求io_submit()后,它就不需要去等待,讓后臺(tái)給它搞定讀寫。之后本線程或者其他線程就可以通過io_getevents()去同步I/O的結(jié)果。
這樣的AIO有一個(gè)極大的好處在于,IO不會(huì)阻塞住CPU的行為,有利于充分利用硬件的資源,有利于讓CPU、IO都parallel起來 。當(dāng)然,同樣的動(dòng)作,似乎用epoll()、SIGIO也可以呈現(xiàn)出來。尤其是epoll(),幾乎是C10K問題解決方案在Linux的代名詞。epoll_wait()先等待IO請(qǐng)求的read、write可以發(fā)生,而后再根據(jù)返回的事件發(fā)起讀寫請(qǐng)求:
事件驅(qū)動(dòng)模型libevent等,看起來是事件到來,callback被執(zhí)行的Reactor模式:
但是其底層其實(shí)也是靠epoll()來實(shí)現(xiàn),這個(gè)我們透過strace就可以看出。請(qǐng)見我的3分鐘小電影:
大不一樣
epoll()本質(zhì)上其實(shí)還是先等待IO的讀寫可以發(fā)生,而后再以Linux常規(guī)read()、write() API去發(fā)起IO請(qǐng)求。而AIO則是不管三七二十一,直接發(fā)IO請(qǐng)求,但是并不等待這個(gè)請(qǐng)求的結(jié)束,讓Linux后臺(tái)自己去完成讀寫。我們來看一個(gè)典型的AIO編程案例:
它是通過io_submit()把IO請(qǐng)求發(fā)出去之后,它并不需要等IO的結(jié)束。后面用io_getevents()去同步。上面的代碼中,io_getevents()的代碼與io_submit()的代碼擺在一起,但是其實(shí)它們并不需要一定是同一個(gè)線程。
AIO和傳統(tǒng)epoll()的本質(zhì)區(qū)別是,epoll()等方式,它只是一個(gè)事件獲取機(jī)制,獲取事件后,之后的read(), write()還是要走Linux的傳統(tǒng)路線,經(jīng)過Linux內(nèi)核本身的各個(gè)層次(如page cache,IO調(diào)度等)。而AIO是骨子里面,自己就是一個(gè)IO的方式,最終沒有經(jīng)過傳統(tǒng)的Linux read(),write()這種"all is file"的類VFS接口。Linux native的AIO本身call的函數(shù),本身就是系統(tǒng)調(diào)用。strace執(zhí)行AIO動(dòng)作的進(jìn)程得到的直接就是類似如下的結(jié)果:
strace ./aio....
...
io_setup(128, {3077799936}) = 0
io_submit(3077799936, 1, 0xbfa5e730) = 1
io_getevents(-1217167360, 1, 1, {...}NULL) = 1
在ARM Linux的系統(tǒng)調(diào)用表里也可以看出:
故而,AIO可以更多地把機(jī)會(huì)交給用戶空間,讓用戶空間根據(jù)自身的IO特點(diǎn)來為自己量身定制IO的行為。AIO一般也直接結(jié)合DIO(direct IO)來使用,進(jìn)一步繞開內(nèi)核本身的IO調(diào)度和cache機(jī)制。
我中意你
那么AIO有什么可能的優(yōu)勢(shì)被數(shù)據(jù)庫所青睞呢?
1. 透過AIO,可以屏蔽掉Linux內(nèi)核底層的page cache。而制定application-level的cache機(jī)制。
我們都知道,Linux會(huì)針對(duì)每個(gè)文件對(duì)應(yīng)的inode,創(chuàng)立一個(gè)address_space,并以Radix樹來組織它的page cache命中情況,page的替換算法,整體是LRU,預(yù)測(cè)頁面本身的活躍度。這個(gè)策略,固然非常符合局部性原理(Locality),但是不能針對(duì)用戶程序本身的特征,進(jìn)行用戶級(jí)的cache。
2.透過AIO(尤其是結(jié)合DIO),可以一定程度上,進(jìn)行用戶級(jí)別的IO scheduling。采用AIO,用戶可以控制發(fā)送給內(nèi)核的IO請(qǐng)求,從而控制誰比誰更重要。內(nèi)核固然有它的IO調(diào)度算法,但是它是比較general的。
3. 透過AIO,可以進(jìn)行用戶級(jí)別的read-ahead和write-behind控制。
我們都知道,Linux內(nèi)核本身會(huì)根據(jù)用戶的讀請(qǐng)求,去預(yù)測(cè)后續(xù)的讀,從而在后續(xù)的讀還沒有發(fā)起的情況下,就提前預(yù)讀。詳見:《宋寶華: 文件讀寫(BIO)波瀾壯闊的一生》,但是這種預(yù)讀的page,并不一定是上層應(yīng)用想要的page。而內(nèi)核的write-behind機(jī)制,也可能導(dǎo)致內(nèi)核累積到很多dirty數(shù)據(jù)后,出現(xiàn)寫磁盤的突發(fā)性洪泛?,F(xiàn)在AIO機(jī)制,我們把這些都交給用戶。
4. 透過AIO,不阻塞地在前臺(tái)線程,直接dispatch IO請(qǐng)求,帶來很好的
scalability。在InnoDB里面,可以透過innodb_use_native_aio來配置使用同步的IO還是AIO,而且它有一番對(duì)比,值得細(xì)細(xì)地品讀。同步IO的時(shí)候,query threads是將IO請(qǐng)求放入queue,由InnoDB后臺(tái)線程的每個(gè)線程處理一個(gè)IO請(qǐng)求。而AIO的時(shí)候,query threads直接發(fā)IO請(qǐng)求。
With synchronous I/O, query threads queue I/O requests, andInnoDBbackground threads retrieve the queued requests one at a time, issuing a synchronous I/O call for each. When an I/O request is completed and the I/O call returns, the InnoDBbackground thread that is handling the request calls an I/O completion routine and returns to process the next request. The number of requests that can be processed in parallel isn, wherenis the number ofInnoDBbackground threads. The number ofInnoDBbackground threads is controlled byinnodb_read_io_threadsand innodb_write_io_threads.
With native AIO, query threads dispatch I/O requests directly to the operating system, thereby removing the limit imposed by the number of background threads.InnoDBbackground threads wait for I/O events to signal completed requests. When a request is completed, a background thread calls an I/O completion routine and resumes waiting for I/O events.
-
cpu
+關(guān)注
關(guān)注
68文章
10876瀏覽量
212124 -
Linux
+關(guān)注
關(guān)注
87文章
11314瀏覽量
209807 -
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3821瀏覽量
64506
原文標(biāo)題:今年是“異型屏”轉(zhuǎn)換年!華映看好手機(jī)市場(chǎng)Q4復(fù)蘇
文章出處:【微信號(hào):zengshouji,微信公眾號(hào):MCA手機(jī)聯(lián)盟】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論