存儲(chǔ)介質(zhì)的性能
https://www.toutiao.com/article/7171834179729080871/?log_from=add01b8f7837d8_1669955867890
話(huà)不多說(shuō),先看一張圖,下圖左邊是磁盤(pán)到內(nèi)存的不同介質(zhì),右邊形象地描述了每種介質(zhì)的讀寫(xiě)速率。一句話(huà)總結(jié)就是越靠近c(diǎn)pu,讀寫(xiě)性能越快。了解了不同硬件介質(zhì)的讀寫(xiě)速率后,你會(huì)發(fā)現(xiàn)零拷貝技術(shù)是多么的香,對(duì)于追求極致性能的讀寫(xiě)系統(tǒng)而言,掌握這個(gè)技術(shù)是多么的優(yōu)秀~
上圖是當(dāng)前主流存儲(chǔ)介質(zhì)的讀寫(xiě)性能,從磁盤(pán)到內(nèi)存、內(nèi)存到緩存、緩存到寄存器,每上一個(gè)臺(tái)階,性能就提升10倍。如果我們打開(kāi)一個(gè)文件去讀里面的內(nèi)容,你會(huì)發(fā)現(xiàn)時(shí)間讀取的時(shí)間是遠(yuǎn)大于磁盤(pán)提供的這個(gè)時(shí)延的,這是為什么呢?問(wèn)題就在內(nèi)核態(tài)和用戶(hù)態(tài)這2個(gè)概念后面深藏的I/O邏輯作怪。
內(nèi)核態(tài):也稱(chēng)為內(nèi)核空間。cpu可以訪問(wèn)內(nèi)存的所有數(shù)據(jù),還控制著外圍設(shè)備的訪問(wèn),例如硬盤(pán)、網(wǎng)卡、鼠標(biāo)、鍵盤(pán)等。cpu也可以將自己從一個(gè)程序切換到另一個(gè)程序。
用戶(hù)態(tài):也稱(chēng)為用戶(hù)空間。只能受限的訪問(wèn)內(nèi)存地址,cpu資源可以被其他程序獲取。
計(jì)算機(jī)資源的管控范圍
坦白地說(shuō)內(nèi)核態(tài)就是一個(gè)高級(jí)管理員,它可以控制整個(gè)資源的權(quán)限,用戶(hù)態(tài)就是一個(gè)業(yè)務(wù),每個(gè)人都可以使用它。那計(jì)算機(jī)為啥要這么分呢?且看下文......
由于需要限制不同的程序之間的訪問(wèn)能力, 防止他們獲取別的程序的內(nèi)存數(shù)據(jù), 或者獲取外圍設(shè)備的數(shù)據(jù), 并發(fā)送到網(wǎng)絡(luò)。CPU劃分出兩個(gè)權(quán)限等級(jí):用戶(hù)態(tài)和內(nèi)核態(tài)。
32 位操作系統(tǒng)和 64 位操作系統(tǒng)的虛擬地址空間大小是不同的,在 Linux 操作系統(tǒng)中,虛擬地址空間的內(nèi)部又被分為內(nèi)核空間和用戶(hù)空間兩部分,如下所示:
通過(guò)這里可以看出:
32 位系統(tǒng)的內(nèi)核空間占用 1G,位于最高處,剩下的 3G 是用戶(hù)空間;
64 位系統(tǒng)的內(nèi)核空間和用戶(hù)空間都是 128T,分別占據(jù)整個(gè)內(nèi)存空間的最高和最低處,剩下的中間部分是未定義的。
內(nèi)核態(tài)控制的是內(nèi)核空間的資源管理,用戶(hù)態(tài)訪問(wèn)的是用戶(hù)空間內(nèi)的資源。
從用戶(hù)態(tài)到內(nèi)核態(tài)切換可以通過(guò)三種方式:
系統(tǒng)調(diào)用,其實(shí)系統(tǒng)調(diào)用本身就是中斷,但是軟件中斷,跟硬中斷不同。
異常:如果當(dāng)前進(jìn)程運(yùn)行在用戶(hù)態(tài),如果這個(gè)時(shí)候發(fā)生了異常事件,就會(huì)觸發(fā)切換。例如:缺頁(yè)異常。
外設(shè)中斷:當(dāng)外設(shè)完成用戶(hù)的請(qǐng)求時(shí),會(huì)向CPU發(fā)送中斷信號(hào)。
舉個(gè)例子:當(dāng)計(jì)算機(jī)A上a進(jìn)程要把一個(gè)文件傳送到計(jì)算機(jī)B上的b進(jìn)程空間里面去,它是怎么做的呢?在當(dāng)前的計(jì)算機(jī)系統(tǒng)架構(gòu)下,它的I/O路徑如下圖所示:
計(jì)算機(jī)A的進(jìn)程a先要通過(guò)系統(tǒng)調(diào)用Read(內(nèi)核態(tài))打開(kāi)一個(gè)磁盤(pán)上的文件,這個(gè)時(shí)候就要把數(shù)據(jù)copy一次到內(nèi)核態(tài)的PageCache中,進(jìn)入了內(nèi)核態(tài);
進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Page Cache 搬運(yùn)到用戶(hù)空間的緩沖區(qū),進(jìn)入用戶(hù)態(tài);
進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從用戶(hù)空間的緩沖區(qū)搬運(yùn)到內(nèi)核空間的 Socket(資源由內(nèi)核管控) 緩沖區(qū)中,進(jìn)入內(nèi)核態(tài)。
進(jìn)程a負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Socket 緩沖區(qū)搬運(yùn)到的網(wǎng)絡(luò)中,進(jìn)入用戶(hù)態(tài);
從以上4個(gè)步驟我們可以發(fā)現(xiàn),正是因?yàn)橛脩?hù)態(tài)沒(méi)法控制磁盤(pán)和網(wǎng)絡(luò)資源,所以需要來(lái)回的在內(nèi)核態(tài)切換。這樣一個(gè)發(fā)送文件的過(guò)程就產(chǎn)生了4 次上下文切換:
read 系統(tǒng)調(diào)用讀磁盤(pán)上的文件時(shí):用戶(hù)態(tài)切換到內(nèi)核態(tài);
read 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶(hù)態(tài);
write 系統(tǒng)調(diào)用寫(xiě)到socket時(shí):用戶(hù)態(tài)切換到內(nèi)核態(tài);
write 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶(hù)態(tài)。
如此笨拙的設(shè)計(jì),我們覺(jué)得計(jì)算機(jī)是不是太幼稚了,為啥要來(lái)回切換不能直接在用戶(hù)態(tài)做數(shù)據(jù)傳輸嗎?
CPU 全程負(fù)責(zé)內(nèi)存內(nèi)的數(shù)據(jù)拷貝,參考磁盤(pán)介質(zhì)的讀寫(xiě)性能,這個(gè)操作是可以接受的,但是如果要讓內(nèi)存的數(shù)據(jù)和磁盤(pán)來(lái)回拷貝,這個(gè)時(shí)間消耗就非常的難看,因?yàn)榇疟P(pán)、網(wǎng)卡的速度遠(yuǎn)小于內(nèi)存,內(nèi)存又遠(yuǎn)遠(yuǎn)小于 CPU;
4 次 copy + 4 次上下文切換,代價(jià)太高。
所以計(jì)算機(jī)體系結(jié)構(gòu)的大佬們就想到了能不能單獨(dú)地做一個(gè)模塊來(lái)專(zhuān)職負(fù)責(zé)這個(gè)數(shù)據(jù)的傳輸,不因?yàn)檎加胏pu而降低系統(tǒng)的吞吐呢?方案就是引入了DMA(Direct memory access)
沒(méi)有 DMA ,計(jì)算機(jī)程序訪問(wèn)磁盤(pán)上的數(shù)據(jù)I/O 的過(guò)程是這樣的:
CPU 先發(fā)出讀指令給磁盤(pán)控制器(發(fā)出一個(gè)系統(tǒng)調(diào)用),然后返回;
磁盤(pán)控制器接受到指令,開(kāi)始準(zhǔn)備數(shù)據(jù),把數(shù)據(jù)拷貝到磁盤(pán)控制器的內(nèi)部緩沖區(qū)中,然后產(chǎn)生一個(gè)中斷;
CPU 收到中斷信號(hào)后,讓出CPU資源,把磁盤(pán)控制器的緩沖區(qū)的數(shù)據(jù)一次一個(gè)字節(jié)地拷貝進(jìn)自己的寄存器,然后再把寄存器里的數(shù)據(jù)拷貝到內(nèi)存,而在數(shù)據(jù)傳輸?shù)钠陂g CPU 是無(wú)法執(zhí)行其他任務(wù)的。
可以看到,整個(gè)數(shù)據(jù)的傳輸有幾個(gè)問(wèn)題:一是數(shù)據(jù)在不同的介質(zhì)之間被拷貝了多次;二是每個(gè)過(guò)程都要需要 CPU 親自參與(搬運(yùn)數(shù)據(jù)的過(guò)程),在這個(gè)過(guò)程,在數(shù)據(jù)拷貝沒(méi)有完成前,CPU 是不能做額外事情的,被IO獨(dú)占。
如果I/O操作能比較快的完成,比如簡(jiǎn)單的字符數(shù)據(jù),那沒(méi)問(wèn)題。如果我們用萬(wàn)兆網(wǎng)卡或者硬盤(pán)傳輸大量數(shù)據(jù),CPU就會(huì)一直被占用,其他服務(wù)無(wú)法使用,對(duì)單核系統(tǒng)是致命的。
為了解決上面的CPU被持續(xù)占用的問(wèn)題,大佬們就提出了 DMA 技術(shù),即直接內(nèi)存訪問(wèn)(Direct Memory Access) 技術(shù)。
那到底什么是 DMA 技術(shù)?
所謂的 DMA(Direct Memory Access,即直接存儲(chǔ)器訪問(wèn))其實(shí)是一個(gè)硬件技術(shù),其主要目的是減少大數(shù)據(jù)量傳輸時(shí)的 CPU 消耗,從而提高 CPU 利用效率。其本質(zhì)上是一個(gè)主板和 IO 設(shè)備上的 DMAC 芯片。CPU 通過(guò)調(diào)度 DMAC 可以不參與磁盤(pán)緩沖區(qū)到內(nèi)核緩沖區(qū)的數(shù)據(jù)傳輸消耗,從而提高效率。
那有了DMA,數(shù)據(jù)讀取過(guò)程是怎么樣的呢?下面我們來(lái)具體看看。
詳細(xì)過(guò)程:
用戶(hù)進(jìn)程a調(diào)用系統(tǒng)調(diào)用read 方法,向OS內(nèi)核(資源總管)發(fā)出 I/O 請(qǐng)求,請(qǐng)求讀取數(shù)據(jù)到自己的內(nèi)存緩沖區(qū)中,進(jìn)程進(jìn)入阻塞狀態(tài);
OS內(nèi)核收到請(qǐng)求后,進(jìn)一步將 I/O 請(qǐng)求發(fā)送 DMA,然后讓 CPU 執(zhí)行其他任務(wù);
DMA 再將 I/O 請(qǐng)求發(fā)送給磁盤(pán)控制器;
磁盤(pán)控制器收到 DMA 的 I/O 請(qǐng)求,把數(shù)據(jù)從磁盤(pán)拷貝到磁盤(pán)控制器的緩沖區(qū)中,當(dāng)磁盤(pán)控制器的緩沖區(qū)被寫(xiě)滿(mǎn)后,它向 DMA 發(fā)起中斷信號(hào),告知自己緩沖區(qū)已滿(mǎn);
DMA 收到磁盤(pán)的中斷信號(hào)后,將磁盤(pán)控制器緩沖區(qū)中的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)中,此時(shí)不占用 CPU,CPU 可以執(zhí)行其他任務(wù);
當(dāng) DMA 讀取了一個(gè)固定buffer的數(shù)據(jù),就會(huì)發(fā)送中斷信號(hào)給 CPU;
CPU 收到 DMA 的信號(hào),知道數(shù)據(jù)已經(jīng)Ready,于是將數(shù)據(jù)從內(nèi)核拷貝到用戶(hù)空間,結(jié)束系統(tǒng)調(diào)用;
DMA技術(shù)就是釋放了CPU的占用時(shí)間,它只做事件通知,數(shù)據(jù)拷貝完全由DMA完成。雖然DMA優(yōu)化了CPU的利用率,但是并沒(méi)有提高數(shù)據(jù)讀取的性能。為了減少數(shù)據(jù)在2種狀態(tài)之間的切換次數(shù),因?yàn)闋顟B(tài)切換是一個(gè)非常、非常、非常繁重的工作。為此,大佬們就提了零拷貝技術(shù)。
常見(jiàn)的有2種,而今引入持久化內(nèi)存后,還有APP直接訪問(wèn)內(nèi)存數(shù)據(jù)的方式,這里先不展開(kāi)。下面介紹常用的2種方案,它們的目的減少“上下文切換”和“數(shù)據(jù)拷貝”的次數(shù)。
mmap + write(系統(tǒng)調(diào)用)
sendfile
主要目的,減少數(shù)據(jù)的拷貝
read() 系統(tǒng)調(diào)用:把內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到用戶(hù)的緩沖區(qū)里,用 mmap() 替換 read() ,mmap() 直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)映射到用戶(hù)空間,減少這一次拷貝。
buf = mmap(file, len);write(sockfd, buf, len);
具體過(guò)程如下:
應(yīng)用進(jìn)程調(diào)用了 mmap() 后,DMA 會(huì)把磁盤(pán)的數(shù)據(jù)拷貝到內(nèi)核的緩沖區(qū)里。因?yàn)榻⒘诉@個(gè)內(nèi)存的mapping,所以用戶(hù)態(tài)的數(shù)據(jù)可以直接訪問(wèn)了;
應(yīng)用進(jìn)程再調(diào)用 write(),CPU將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到 socket 緩沖區(qū)中,這一切都發(fā)生在內(nèi)核態(tài)
DMA把內(nèi)核的 socket 緩沖區(qū)里的數(shù)據(jù),拷貝到網(wǎng)卡的緩沖區(qū)里
由上可知,系統(tǒng)調(diào)用mmap() 來(lái)代替 read(), 可以減少一次數(shù)據(jù)拷貝。那我們是否還有優(yōu)化的空間呢?畢竟用戶(hù)態(tài)和內(nèi)核態(tài)仍然需要 4 次上下文切換,系統(tǒng)調(diào)用還是 2 次。那繼續(xù)研究下是否還能繼續(xù)減少切換和數(shù)據(jù)拷貝呢?答案是確定的:可以
Linux 內(nèi)核版本 2.1 提供了一個(gè)專(zhuān)門(mén)發(fā)送文件的系統(tǒng)調(diào)用函數(shù) sendfile(),函數(shù)形式如下:
#include <sys/socket.h>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
參數(shù)說(shuō)明:
前2個(gè)參數(shù)分別是目的端和源端的文件描述符,
后2個(gè)參數(shù)是源端的偏移量和復(fù)制數(shù)據(jù)的長(zhǎng)度,返回值是實(shí)際復(fù)制數(shù)據(jù)的長(zhǎng)度。
首先,使用sendfile()可以替代前面的 read() 和 write() 這兩個(gè)系統(tǒng)調(diào)用,減少一次系統(tǒng)調(diào)用和 2 次上下文切換。
其次,sendfile可以直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)拷貝到 socket 緩沖區(qū)里,不再拷貝到用戶(hù)態(tài),優(yōu)化后只有 2 次上下文切換,和 3 次數(shù)據(jù)拷貝。如下圖:
盡管如此,我們還是又?jǐn)?shù)據(jù)拷貝,這不符合我們的標(biāo)題目標(biāo)。如果網(wǎng)卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技術(shù),我們就可以進(jìn)一步減少通過(guò) CPU 把內(nèi)核緩沖區(qū)里的數(shù)據(jù)拷貝到 socket 緩沖區(qū)的過(guò)程。
我們可以在 Linux 系統(tǒng)下通過(guò)下面的命令,查看網(wǎng)卡是否支持 scatter-gather 特性:
$ ethtool -k eth0 | grep scatter-gather scatter-gather: on
于是,從 Linux 內(nèi)核 2.4 版本開(kāi)始起,對(duì)于支持網(wǎng)卡支持 SG-DMA 技術(shù)的情況下, sendfile() 系統(tǒng)調(diào)用的過(guò)程發(fā)生了點(diǎn)變化,具體過(guò)程如下:
通過(guò) DMA 將磁盤(pán)上的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)里;
緩沖區(qū)描述符和數(shù)據(jù)長(zhǎng)度傳到 socket 緩沖區(qū),這樣網(wǎng)卡的 SG-DMA 控制器就可以直接將內(nèi)核緩存中的數(shù)據(jù)拷貝到網(wǎng)卡的緩沖區(qū)里;
在這個(gè)過(guò)程之中,實(shí)際上只進(jìn)行了 2 次數(shù)據(jù)拷貝,如下圖:
這就是零拷貝(Zero-copy)技術(shù),因?yàn)槲覀儧](méi)有在內(nèi)存層面去拷貝數(shù)據(jù),也就是說(shuō)全程沒(méi)有通過(guò) CPU 來(lái)搬運(yùn)數(shù)據(jù),所有的數(shù)據(jù)都是通過(guò) DMA 來(lái)進(jìn)行傳輸?shù)摹?/strong>
零拷貝技術(shù)的文件傳輸方式相比傳統(tǒng)文件傳輸?shù)姆绞剑?strong>只需要 2 次上下文切換和數(shù)據(jù)拷貝次數(shù),就可以完成文件的傳輸,而且 2 次的數(shù)據(jù)拷貝過(guò)程,都不需要通過(guò) CPU,2 次都是由 DMA 來(lái)搬運(yùn)。
所以,零拷貝技術(shù)可以把文件傳輸?shù)男阅芴岣咧辽僖槐丁?/strong>
回顧第一節(jié)的存儲(chǔ)介質(zhì)的性能,如果我們總是在磁盤(pán)和內(nèi)存間傳輸數(shù)據(jù),一個(gè)大文件的跨機(jī)器傳輸肯定會(huì)讓你抓狂。那有什么方法加速呢?直觀的想法就是建立一個(gè)離CPU近的一個(gè)臨時(shí)通道,這樣就可以加速文件的傳輸。 這個(gè)通道就是我們前文提到的「內(nèi)核緩沖區(qū)」,這個(gè)「內(nèi)核緩沖區(qū)」實(shí)際上是磁盤(pán)高速緩存(PageCache)。
零拷貝就是使用了DMA + PageCache 技術(shù)提升了性能,我們來(lái)看看 PageCache 是如何做到的。
從開(kāi)篇的介質(zhì)性能看,磁盤(pán)相比內(nèi)存讀寫(xiě)的速度要慢很多,所以?xún)?yōu)化的思路就是盡量的把「讀寫(xiě)磁盤(pán)」替換成「讀寫(xiě)內(nèi)存」。因此通過(guò) DMA 把磁盤(pán)里的數(shù)據(jù)搬運(yùn)到內(nèi)存里,轉(zhuǎn)為直接讀內(nèi)存,這樣就快多了。但是內(nèi)存的空間是有限的,成本也比磁盤(pán)貴,它只能拷貝磁盤(pán)里的一小部分?jǐn)?shù)據(jù)。
那就不可避免的產(chǎn)生一個(gè)問(wèn)題,到底選擇哪些磁盤(pán)數(shù)據(jù)拷貝到內(nèi)存呢?
從業(yè)務(wù)的視角來(lái)看,業(yè)務(wù)的數(shù)據(jù)有冷熱之分,我們通過(guò)一些的淘汰算法可以知道哪些是熱數(shù)據(jù),因?yàn)閿?shù)據(jù)訪問(wèn)的時(shí)序性,被訪問(wèn)過(guò)的數(shù)據(jù)可能被再次訪問(wèn)的概率很高,于是我們可以用 PageCache 來(lái)緩存最近被訪問(wèn)的數(shù)據(jù),當(dāng)空間不足時(shí)淘汰最久未被訪問(wèn)的數(shù)據(jù)。
讀Cache
當(dāng)內(nèi)核發(fā)起一個(gè)讀請(qǐng)求時(shí)(例如進(jìn)程發(fā)起read()請(qǐng)求),首先會(huì)檢查請(qǐng)求的數(shù)據(jù)是否緩存到了Page Cache中。如果有,那么直接從內(nèi)存中讀取,不需要訪問(wèn)磁盤(pán),這被稱(chēng)為cache命中(cache hit);如果cache中沒(méi)有請(qǐng)求的數(shù)據(jù),即cache未命中(cache miss),就必須從磁盤(pán)中讀取數(shù)據(jù)。然后內(nèi)核將讀取的數(shù)據(jù)緩存到cache中,這樣后續(xù)的讀請(qǐng)求就可以命中cache了。
page可以只緩存一個(gè)文件部分的內(nèi)容,不需要把整個(gè)文件都緩存進(jìn)來(lái)。
寫(xiě)Cache
當(dāng)內(nèi)核發(fā)起一個(gè)寫(xiě)請(qǐng)求時(shí)(例如進(jìn)程發(fā)起write()請(qǐng)求),同樣是直接往cache中寫(xiě)入,后備存儲(chǔ)中的內(nèi)容不會(huì)直接更新(當(dāng)服務(wù)器出現(xiàn)斷電關(guān)機(jī)時(shí),存在數(shù)據(jù)丟失風(fēng)險(xiǎn))。
內(nèi)核會(huì)將被寫(xiě)入的page標(biāo)記為dirty,并將其加入dirty list中。內(nèi)核會(huì)周期性地將dirty list中的page寫(xiě)回到磁盤(pán)上,從而使磁盤(pán)上的數(shù)據(jù)和內(nèi)存中緩存的數(shù)據(jù)一致。
當(dāng)滿(mǎn)足以下兩個(gè)條件之一將觸發(fā)臟數(shù)據(jù)刷新到磁盤(pán)操作:
數(shù)據(jù)存在的時(shí)間超過(guò)了dirty_expire_centisecs(默認(rèn)300厘秒,即30秒)時(shí)間;
臟數(shù)據(jù)所占內(nèi)存 > dirty_background_ratio,也就是說(shuō)當(dāng)臟數(shù)據(jù)所占用的內(nèi)存占總內(nèi)存的比例超過(guò)dirty_background_ratio(默認(rèn)10,即系統(tǒng)內(nèi)存的10%)的時(shí)候會(huì)觸發(fā)pdflush刷新臟數(shù)據(jù)。
還有一點(diǎn),現(xiàn)在的磁盤(pán)是擦除式讀寫(xiě),每次需要讀一個(gè)固定的大小,隨機(jī)讀取帶來(lái)的磁頭尋址會(huì)增加時(shí)延,為了降低它的影響,PageCache 使用了「預(yù)讀功能」。
在某些應(yīng)用場(chǎng)景下,比如我們每次打開(kāi)文件只需要讀取或者寫(xiě)入幾個(gè)字節(jié)的情況,會(huì)比Direct I/O多一些磁盤(pán)的讀取于寫(xiě)入。
舉個(gè)例子,假設(shè)每次我們要讀 32 KB 的字節(jié),read填充到用戶(hù)buffer的大小是0~32KB,但內(nèi)核會(huì)把其后面的 32~64 KB 也讀取到 PageCache,這樣后面讀取 32~64 KB 的成本就很低,如果在 32~64 KB 淘汰出 PageCache 前,進(jìn)程需要讀這些數(shù)據(jù),對(duì)比分塊讀取的方式,這個(gè)策略收益就非常大。
Page Cache的優(yōu)勢(shì)與劣勢(shì)
優(yōu)勢(shì)
加快對(duì)數(shù)據(jù)的訪問(wèn)
減少磁盤(pán)I/O的訪問(wèn)次數(shù),提高系統(tǒng)磁盤(pán)壽命
減少對(duì)磁盤(pán)I/O的訪問(wèn),提高系統(tǒng)磁盤(pán)I/O吞吐量(Page Cache的預(yù)讀機(jī)制)
劣勢(shì)
使用額外的物理內(nèi)存空間,當(dāng)物理內(nèi)存比較緊俏的時(shí)候,可能會(huì)導(dǎo)致頻繁的swap操作,最終會(huì)導(dǎo)致系統(tǒng)的磁盤(pán)I/O負(fù)載上升。
Page Cache沒(méi)有給應(yīng)用層提供一個(gè)很好的API。導(dǎo)致應(yīng)用層想要優(yōu)化Page Cache的使用策略很難。因此一些應(yīng)用實(shí)現(xiàn)了自己的Page管理,比如MySQL的InnoDB存儲(chǔ)引擎以16KB的頁(yè)進(jìn)行管理。
另外,由于文件太大,可能某些部分的文件數(shù)據(jù)已經(jīng)被淘汰出去了,這樣就會(huì)帶來(lái) 2 個(gè)問(wèn)題:
PageCache 由于長(zhǎng)時(shí)間被大文件的部分塊占據(jù),而導(dǎo)致一些「熱點(diǎn)」的小文件可能就無(wú)法常駐 PageCache,導(dǎo)致頻繁讀寫(xiě)磁盤(pán)而引起性能下降;
PageCache 中的大文件數(shù)據(jù),由于沒(méi)有全部常駐內(nèi)存,只有部分無(wú)法享受到緩存帶來(lái)的好處,同時(shí)過(guò)多的DMA 拷貝動(dòng)作,增加了時(shí)延;
因此針對(duì)大文件的傳輸,不應(yīng)該使用 PageCache。
Page Cache緩存查看工具:cachestat
PageCache的參數(shù)調(diào)優(yōu)
備注:不同硬件配置的服務(wù)器可能效果不同,所以,具體的參數(shù)值設(shè)置需要考慮自己集群硬件配置。
考慮的因素主要包括:CPU核數(shù)、內(nèi)存大小、硬盤(pán)類(lèi)型、網(wǎng)絡(luò)帶寬等。
查看Page Cache參數(shù): sysctl -a|grep dirty
調(diào)整內(nèi)核參數(shù)來(lái)優(yōu)化IO性能?
vm.dirty_background_ratio參數(shù)優(yōu)化:當(dāng)cached中緩存當(dāng)數(shù)據(jù)占總內(nèi)存的比例達(dá)到這個(gè)參數(shù)設(shè)定的值時(shí)將觸發(fā)刷磁盤(pán)操作。把這個(gè)參數(shù)適當(dāng)調(diào)小,這樣可以把原來(lái)一個(gè)大的IO刷盤(pán)操作變?yōu)槎鄠€(gè)小的IO刷盤(pán)操作,從而把IO寫(xiě)峰值削平。對(duì)于內(nèi)存很大和磁盤(pán)性能比較差的服務(wù)器,應(yīng)該把這個(gè)值設(shè)置的小一點(diǎn)。
vm.dirty_ratio參數(shù)優(yōu)化:對(duì)于寫(xiě)壓力特別大的,建議把這個(gè)參數(shù)適當(dāng)調(diào)大;對(duì)于寫(xiě)壓力小的可以適當(dāng)調(diào)??;如果cached的數(shù)據(jù)所占比例(這里是占總內(nèi)存的比例)超過(guò)這個(gè)設(shè)置,系統(tǒng)會(huì)停止所有的應(yīng)用層的IO寫(xiě)操作,等待刷完數(shù)據(jù)后恢復(fù)IO。所以萬(wàn)一觸發(fā)了系統(tǒng)的這個(gè)操作,對(duì)于用戶(hù)來(lái)說(shuō)影響非常大的。
vm.dirty_expire_centisecs參數(shù)優(yōu)化:這個(gè)參數(shù)會(huì)和參數(shù)vm.dirty_background_ratio一起來(lái)作用,一個(gè)表示大小比例,一個(gè)表示時(shí)間;即滿(mǎn)足其中任何一個(gè)的條件都達(dá)到刷盤(pán)的條件。
vm.dirty_writeback_centisecs參數(shù)優(yōu)化:理論上調(diào)小這個(gè)參數(shù),可以提高刷磁盤(pán)的頻率,從而盡快把臟數(shù)據(jù)刷新到磁盤(pán)上。但一定要保證間隔時(shí)間內(nèi)一定可以讓數(shù)據(jù)刷盤(pán)完成。
vm.swappiness參數(shù)優(yōu)化:禁用swap空間,設(shè)置vm.swappiness=0
我們先來(lái)回顧下前文的讀流程,當(dāng)調(diào)用 read 方法讀取文件時(shí),如果數(shù)據(jù)沒(méi)有準(zhǔn)備好,進(jìn)程會(huì)阻塞在 read 方法調(diào)用,要等待磁盤(pán)數(shù)據(jù)的返回,如下圖:
具體過(guò)程:
當(dāng)調(diào)用 read 方法時(shí),切到內(nèi)核態(tài)訪問(wèn)磁盤(pán)資源。此時(shí)內(nèi)核會(huì)向磁盤(pán)發(fā)起 I/O 請(qǐng)求,磁盤(pán)收到請(qǐng)求后,準(zhǔn)備數(shù)據(jù)。數(shù)據(jù)讀取到控制器緩沖區(qū)完成后,就會(huì)向內(nèi)核發(fā)起 I/O 中斷,通知內(nèi)核磁盤(pán)數(shù)據(jù)已經(jīng)準(zhǔn)備好;
內(nèi)核收到 I/O 中斷后,將數(shù)據(jù)從磁盤(pán)控制器緩沖區(qū)拷貝到 PageCache 里;
內(nèi)核把 PageCache 中的數(shù)據(jù)拷貝到用戶(hù)緩沖區(qū),read 調(diào)用返回成功。
對(duì)于大塊數(shù)傳輸導(dǎo)致的阻塞,可以用異步 I/O 來(lái)解決,如下圖:
分為兩步執(zhí)行:
內(nèi)核向磁盤(pán)發(fā)起讀請(qǐng)求,因?yàn)槭钱惒秸?qǐng)求可以不等待數(shù)據(jù)就位就可以返回,于是CPU釋放出來(lái)可以處理其他任務(wù);
當(dāng)內(nèi)核將磁盤(pán)中的數(shù)據(jù)拷貝到進(jìn)程緩沖區(qū)后,進(jìn)程將接收到內(nèi)核的通知,再去處理數(shù)據(jù);
從上面流程來(lái)看,異步 I/O 并沒(méi)有讀寫(xiě) PageCache,繞開(kāi) PageCache 的 I/O 叫直接 I/O,使用 PageCache 的 I/O 則叫緩存 I/O。通常,對(duì)于磁盤(pán)異步 I/O 只支持直接 I/O。
因此,在高并發(fā)的場(chǎng)景下,針對(duì)大文件的傳輸?shù)姆绞?,?yīng)該使用「異步 I/O + 直接 I/O」來(lái)替代零拷貝技術(shù)。
直接 I/O 的兩種場(chǎng)景:
應(yīng)用程序已經(jīng)實(shí)現(xiàn)了磁盤(pán)數(shù)據(jù)的緩存
大文件傳輸
聯(lián)系客服