溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)

發(fā)布時(shí)間:2022-01-12 17:48:19 來(lái)源:億速云 閱讀:140 作者:柒染 欄目:服務(wù)器

這篇文章跟大家分析一下“如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)”。內(nèi)容詳細(xì)易懂,對(duì)“如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)”感興趣的朋友可以跟著小編的思路慢慢深入來(lái)閱讀一下,希望閱讀后能夠?qū)Υ蠹矣兴鶐椭?。下面跟著小編一起深入學(xué)習(xí)“如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)”的知識(shí)吧。

背景

隨著企業(yè)SDK在多條產(chǎn)品線的廣泛使用,隨著SDK開(kāi)發(fā)人員的增長(zhǎng),每日往SDK提交的補(bǔ)丁量與日俱增,自動(dòng)化提交代碼檢查的壓力已經(jīng)明顯超過(guò)了通用服務(wù)器的負(fù)載。于是向公司申請(qǐng)了一臺(tái)專(zhuān)用服務(wù)器,用于SDK構(gòu)建檢查。

$ cat /proc/cpuinfo | grep ^proccessor | wc -l 48 $ free -h              total       used       free     shared    buffers     cached Mem:           47G        45G       1.6G        20M       7.7G        25G -/+ buffers/cache:        12G        35G Swap:           0B         0B         0B $ df 文件系統(tǒng)                          容量  已用  可用 已用% 掛載點(diǎn) ...... /dev/sda1                          98G   14G   81G   15% / /dev/vda1                         2.9T  1.8T  986G   65% /home

這是KVM虛擬的服務(wù)器,提供了CPU 48線程,實(shí)際可用47G內(nèi)存,磁盤(pán)空間約達(dá)到3TB。

由于獨(dú)享服務(wù)器所有資源,設(shè)置了十來(lái)個(gè)worker并行編譯,從提交補(bǔ)丁到發(fā)送編譯結(jié)果的速度杠杠的。但是在補(bǔ)丁提交非常多的時(shí)候,速度瞬間就慢了下去,一次提交觸發(fā)的編譯甚至要1個(gè)多小時(shí)。通過(guò)top看到CPU負(fù)載并不高,難道是IO瓶頸?找IT要到了root權(quán)限,干起來(lái)!

由于認(rèn)知的局限性,如有考慮不周的地方,希望一起交流學(xué)習(xí)

整體認(rèn)識(shí)IO棧

如果有完整的IO棧的認(rèn)識(shí),無(wú)疑有助于更細(xì)膩的優(yōu)化IO。循著IO棧從上往下的順序,我們逐層分析可優(yōu)化的地方。

在網(wǎng)上有Linux完整的IO棧結(jié)構(gòu)圖,但太過(guò)完整反而不容易理解。按我的認(rèn)識(shí),簡(jiǎn)化過(guò)后的IO棧應(yīng)該是下圖的模樣。

如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)
  1. 用戶(hù)空間:除了用戶(hù)自己的APP之外,也隱含了所有的庫(kù),例如常見(jiàn)的C庫(kù)。我們常用的IO函數(shù),例如open()/read()/write()是系統(tǒng)調(diào)用,由內(nèi)核直接提供功能實(shí)現(xiàn),而fopen()/fread()/fwrite()則是C庫(kù)實(shí)現(xiàn)的函數(shù),通過(guò)封裝系統(tǒng)調(diào)用實(shí)現(xiàn)更高級(jí)的功能。

  2. 虛擬文件系統(tǒng):屏蔽具體文件系統(tǒng)的差異,向用戶(hù)空間提供統(tǒng)一的入口。具體的文件系統(tǒng)通過(guò)register_filesystem()向虛擬文件系統(tǒng)注冊(cè)掛載鉤子,在用戶(hù)掛載具體的文件系統(tǒng)時(shí),通過(guò)回調(diào)掛載鉤子實(shí)現(xiàn)文件系統(tǒng)的初始化。虛擬文件系統(tǒng)提供了inode來(lái)記錄文件的元數(shù)據(jù),dentry記錄了目錄項(xiàng)。對(duì)用戶(hù)空間,虛擬文件系統(tǒng)注冊(cè)了系統(tǒng)調(diào)用,例如SYSCALL_DEFINE3(open,  const char __user *, filename, int, flags, umode_t, mode)注冊(cè)了open()的系統(tǒng)調(diào)用。

  3. 具體的文件系統(tǒng):文件系統(tǒng)要實(shí)現(xiàn)存儲(chǔ)空間的管理,換句話說(shuō),其規(guī)劃了哪些空間存儲(chǔ)了哪些文件的數(shù)據(jù),就像一個(gè)個(gè)收納盒,A文件保存在這個(gè)塊,B文件則放在哪個(gè)塊。不同的管理策略以及其提供的不同功能,造就了各式各樣的文件系統(tǒng)。除了類(lèi)似于vfat、ext4、btrfs等常見(jiàn)的塊設(shè)備文件系統(tǒng)之外,還有sysfs、procfs、pstorefs、tempfs等構(gòu)建在內(nèi)存上的文件系統(tǒng),也有yaffs,ubifs等構(gòu)建在Flash上的文件系統(tǒng)。

  4. 頁(yè)緩存:可以簡(jiǎn)單理解為一片存儲(chǔ)著磁盤(pán)數(shù)據(jù)的內(nèi)存,不過(guò)其內(nèi)部是以頁(yè)為管理單元,常見(jiàn)的頁(yè)大小是4K。這片內(nèi)存的大小不是固定的,每有一筆新的數(shù)據(jù),則申請(qǐng)一個(gè)新的內(nèi)存頁(yè)。由于內(nèi)存的性能遠(yuǎn)大于磁盤(pán),為了提高IO性能,我們就可以把IO數(shù)據(jù)緩存在內(nèi)存,這樣就可以在內(nèi)存中獲取要的數(shù)據(jù),不需要經(jīng)過(guò)磁盤(pán)讀寫(xiě)的漫長(zhǎng)的等待。申請(qǐng)內(nèi)存來(lái)緩存數(shù)據(jù)簡(jiǎn)單,如何管理所有的頁(yè)緩存以及如何及時(shí)回收緩存頁(yè)才是精髓。

  5. 通用塊層:通用塊層也可以細(xì)分為bio層和request層。頁(yè)緩存以頁(yè)為管理單位,而bio則記錄了磁盤(pán)塊與頁(yè)之間的關(guān)系,一個(gè)磁盤(pán)塊可以關(guān)聯(lián)到多個(gè)不同的內(nèi)存頁(yè)中,通過(guò)submit_bio()提交bio到request層。一個(gè)request可以理解為多個(gè)bio的集合,把多個(gè)地址連續(xù)的bio合并成一個(gè)request。多個(gè)request經(jīng)過(guò)IO調(diào)度算法的合并和排序,有序地往下層提交IO請(qǐng)求。

  6. 設(shè)備驅(qū)動(dòng)與塊設(shè)備:不同塊設(shè)備有不同的使用協(xié)議,而特定的設(shè)備驅(qū)動(dòng)則是實(shí)現(xiàn)了特定設(shè)備需要的協(xié)議以正常驅(qū)使設(shè)備。對(duì)塊設(shè)備而言,塊設(shè)備驅(qū)動(dòng)需要把request解析成一個(gè)個(gè)設(shè)備操作指令,在協(xié)議的規(guī)范下與塊設(shè)備通信來(lái)交換數(shù)據(jù)。

形象點(diǎn)來(lái)說(shuō),發(fā)起一次IO讀請(qǐng)求的過(guò)程是怎么樣的呢?

用戶(hù)空間通過(guò)虛擬文件系統(tǒng)提供的統(tǒng)一的IO系統(tǒng)調(diào)用,從用戶(hù)態(tài)切到內(nèi)核態(tài)。虛擬文件系統(tǒng)通過(guò)調(diào)用具體文件系統(tǒng)注冊(cè)的回調(diào),把需求傳遞到具體的文件系統(tǒng)中。緊接著具體的文件系統(tǒng)根據(jù)自己的管理邏輯,換算到具體的磁盤(pán)塊地址,從頁(yè)緩存尋找塊設(shè)備的緩存數(shù)據(jù)。讀操作一般是同步的,如果在頁(yè)緩存沒(méi)有緩存數(shù)據(jù),則向通用塊層發(fā)起一次磁盤(pán)讀。通用塊層合并和排序所有進(jìn)程產(chǎn)生的的IO請(qǐng)求,經(jīng)過(guò)設(shè)備驅(qū)動(dòng)從塊設(shè)備讀取真正的數(shù)據(jù)。最后是逐層返回。讀取的數(shù)據(jù)既拷貝到用戶(hù)空間的buffer中,也會(huì)在頁(yè)緩存中保留一份副本,以便下次快速訪問(wèn)。

如果 頁(yè)緩存沒(méi)命中,同步都會(huì)一路通到 塊設(shè)備,而對(duì)于 異步寫(xiě),則是把數(shù)據(jù)放到 頁(yè)緩存后返回,由內(nèi)核回刷進(jìn)程在合適時(shí)候回刷到 塊設(shè)備。

根據(jù)這個(gè)流程,考慮到我沒(méi)要到KVM host的權(quán)限,我只能著手從Guest端的IO棧做優(yōu)化,具體包括以下幾個(gè)方面:

  1. 交換分區(qū)(swap)

  2. 文件系統(tǒng)(ext4)

  3. 頁(yè)緩存(Page Cache)

  4. Request層(IO調(diào)度算法)

由于源碼以及編譯的臨時(shí)文件都不大但數(shù)量極其多,對(duì)隨機(jī)IO的要求非常高。要提高隨機(jī)IO的性能,在不改變硬件的情況下,需要緩存更多數(shù)據(jù),以實(shí)現(xiàn)合并更多的IO請(qǐng)求。

咨詢(xún)ITer得知,服務(wù)器都有備用電源,能確保不會(huì)掉電停機(jī)。出于這樣的情況,我們可以盡可能優(yōu)化速度,而不用擔(dān)心掉電導(dǎo)致數(shù)據(jù)丟失問(wèn)題。

總的來(lái)說(shuō),優(yōu)化的核心思路是盡可能多的使用內(nèi)存緩存數(shù)據(jù),盡可能減小不必要的開(kāi)銷(xiāo),例如文件系統(tǒng)為了保證數(shù)據(jù)一致性使用日志造成的開(kāi)銷(xiāo)。

交換分區(qū)

交換分區(qū)的存在,可以讓內(nèi)核在內(nèi)存壓力大時(shí),把內(nèi)核認(rèn)為一些不常用的內(nèi)存置換到交換分區(qū),以此騰出更多的內(nèi)存給系統(tǒng)。在物理內(nèi)存容量不足且運(yùn)行吃?xún)?nèi)存的應(yīng)用時(shí),交換分區(qū)的作用效果是非常明顯的。

然而本次優(yōu)化的服務(wù)器反而不應(yīng)該使用交換分區(qū)。為什么呢?服務(wù)器總內(nèi)存達(dá)到47G,且服務(wù)器除了Jenkins  slave進(jìn)程外沒(méi)有大量吃?xún)?nèi)存的進(jìn)程。從內(nèi)存的使用情況來(lái)看,絕大部分內(nèi)存都是被cache/buffer占用,是可丟棄的文件緩存,因此內(nèi)存是充足的,不需要通過(guò)交換分區(qū)擴(kuò)大虛擬內(nèi)存。

# free -h              total       used       free     shared    buffers     cached Mem:           47G        45G       1.6G        21M        18G        16G -/+ buffers/cache:        10G        36G

交換分區(qū)也是磁盤(pán)的空間,從交換分區(qū)置入置出數(shù)據(jù)可也是要占用IO資源的,與本次IO優(yōu)化目的相悖,因此在此服務(wù)器中,需要取消swap分區(qū)。

查看系統(tǒng)狀態(tài)發(fā)現(xiàn),此服務(wù)器并沒(méi)使能swap。

# cat /proc/swaps  Filename Type Size Used Priority  #

文件系統(tǒng)

用戶(hù)發(fā)起一次讀寫(xiě),經(jīng)過(guò)了虛擬文件系統(tǒng)(VFS)后,交給了實(shí)際的文件系統(tǒng)。

首先查詢(xún)分區(qū)掛載情況:

# mount ... /dev/sda1 on on / type ext4 (rw) /dev/vda1 on /home type ext4 (rw) ...

此服務(wù)器主要有兩個(gè)塊設(shè)備,分別是 sda和 vda。sda 是常見(jiàn)的 SCSI/IDE 設(shè)備,我們個(gè)人PC上如果使用的機(jī)械硬盤(pán),往往就會(huì)是 sda  設(shè)備節(jié)點(diǎn)。vda 是 virtio 磁盤(pán)設(shè)備。由于本服務(wù)器是 KVM 提供的虛擬機(jī),不管是 sda 還是  vda,其實(shí)都是虛擬設(shè)備,差別在于前者是完全虛擬化的塊設(shè)備,后者是半虛擬化的塊設(shè)備。從網(wǎng)上找到的資料來(lái)看,使用半虛擬化的設(shè)備,可以實(shí)現(xiàn)Host與Guest更高效的協(xié)作,從而實(shí)現(xiàn)更高的性能。在此例子中,sda  作為根文件系統(tǒng)使用,vda 則是用于存儲(chǔ)用戶(hù)數(shù)據(jù),在編譯時(shí),主要看的是 vda 分區(qū)的IO情況。

vda 使用 ext4 文件系統(tǒng)。ext4 是目前常見(jiàn)的Linux上使用的穩(wěn)定的文件系統(tǒng),查看其超級(jí)塊信息:

# dumpe2fs /dev/vda1 ... Filesystem features:      has_journal dir_index ... ... Inode count:              196608000 Block count:              786431991 Free inodes:              145220571 Block size:               4096 ...

我猜測(cè)ITer使用的默認(rèn)參數(shù)格式化的分區(qū),為其分配了塊大小為4K,inode數(shù)量達(dá)到19660萬(wàn)個(gè)且使能了日志。

塊大小設(shè)為4K無(wú)可厚非,適用于當(dāng)前源文件偏小的情況,也沒(méi)必要為了更緊湊的空間降低塊大小??臻e inode 達(dá)到 14522萬(wàn),空閑占比達(dá)到  73.86%。當(dāng)前 74% 的空間使用率,inode只使用了26.14%。一個(gè)inode占256B,那么10000萬(wàn)個(gè)inode占用23.84G。inode  實(shí)在太多了,造成大量的空間浪費(fèi)??上?,inode數(shù)量在格式化時(shí)指定,后期無(wú)法修改,當(dāng)前也不能簡(jiǎn)單粗暴地重新格式化。

我們能做什么呢?我們可以從日志和掛載參數(shù)著手優(yōu)化

日志是為了保證掉電時(shí)文件系統(tǒng)的一致性,(ordered日志模式下)通過(guò)把元數(shù)據(jù)寫(xiě)入到日志塊,在寫(xiě)入數(shù)據(jù)后再修改元數(shù)據(jù)。如果此時(shí)掉電,通過(guò)日志記錄可以回滾文件系統(tǒng)到上一個(gè)一致性的狀態(tài),即保證元數(shù)據(jù)與數(shù)據(jù)是匹配的。然而上文有說(shuō),此服務(wù)器有備用電源,不需要擔(dān)心掉電,因此完全可以把日志取消掉。

# tune2fs -O ^has_journal /dev/vda1 tune2fs 1.42.9 (4-Feb-2014) The has_journal feature may only be cleared when the filesystem is unmounted or mounted read-only.

可惜失敗了。由于時(shí)刻有任務(wù)在執(zhí)行,不太好直接umount或者-o  remount,ro,無(wú)法在掛載時(shí)取消日志。既然取消不了,咱們就讓日志減少損耗,就需要修改掛載參數(shù)了。

ext4掛載參數(shù): data

ext4有3種日志模式,分別是ordered,writeback,journal。他們的差別網(wǎng)上有很多資料,我簡(jiǎn)單介紹下:

  1. jorunal:把元數(shù)據(jù)與數(shù)據(jù)一并寫(xiě)入到日志塊。性能差不多折半,因?yàn)閿?shù)據(jù)寫(xiě)了兩次,但最安全

  2. writeback: 把元數(shù)據(jù)寫(xiě)入日志塊,數(shù)據(jù)不寫(xiě)入日志塊,但不保證數(shù)據(jù)先落盤(pán)。性能最高,但由于不保證元數(shù)據(jù)與數(shù)據(jù)的順序,也是掉電最不安全的

  3. ordered:與writeback相似,但會(huì)保證數(shù)據(jù)先落盤(pán),再是元數(shù)據(jù)。這種性能以保證足夠的安全,這是大多數(shù)PC上推薦的默認(rèn)的模式

在不需要擔(dān)心掉電的服務(wù)器環(huán)境,我們完全可以使用writeback的日志模式,以獲取最高的性能。

# mount -o remount,rw,data=writeback /home mount: /home not mounted or bad option # dmesg [235737.532630] EXT4-fs (vda1): Cannot change data mode on remount

沮喪,又是不能動(dòng)態(tài)改,干脆寫(xiě)入到/etc/config,只能寄希望于下次重啟了。

# cat /etc/fstab UUID=...  /home  ext4  defaults,rw,data=writeback...

ext4掛載參數(shù):noatime

Linux上對(duì)每個(gè)文件都記錄了3個(gè)時(shí)間戳

時(shí)間戳全稱(chēng)含義atimeaccess time訪問(wèn)時(shí)間,就是最近一次讀的時(shí)間mtimedata modified  time數(shù)據(jù)修改時(shí)間,就是內(nèi)容最后一次改動(dòng)時(shí)間ctimestatus change time文件狀態(tài)(元數(shù)據(jù))的改變時(shí)間,比如權(quán)限,所有者等

我們編譯執(zhí)行的Make可以根據(jù)修改時(shí)間來(lái)判斷是否要重新編譯,而atime記錄的訪問(wèn)時(shí)間其實(shí)在很多場(chǎng)景下都是多余的。所以,noatime應(yīng)運(yùn)而生。不記錄atime可以大量減少讀造成的元數(shù)據(jù)寫(xiě)入量,而元數(shù)據(jù)的寫(xiě)入往往產(chǎn)生大量的隨機(jī)IO。

# mount -o ...noatime... /home

ext4掛載參數(shù):nobarrier

這主要是決定在日志代碼中是否使用寫(xiě)屏障(write  barrier),對(duì)日志提交進(jìn)行正確的磁盤(pán)排序,使易失性磁盤(pán)寫(xiě)緩存可以安全使用,但會(huì)帶來(lái)一些性能損失。從功能來(lái)看,跟writeback和ordered日志模式非常相似。沒(méi)研究過(guò)這方面的源碼,說(shuō)不定就是一回事。不管怎么樣,禁用寫(xiě)屏障毫無(wú)疑問(wèn)能提高寫(xiě)性能。

# mount -o ...nobarrier... /home

ext4掛載參數(shù):delalloc

delalloc是 delayed allocation  的縮寫(xiě),如果使能,則ext4會(huì)延緩申請(qǐng)數(shù)據(jù)塊直至超時(shí)。為什么要延緩申請(qǐng)呢?在inode中采用多級(jí)索引的方式記錄了文件數(shù)據(jù)所在的數(shù)據(jù)塊編號(hào),如果出現(xiàn)大文件,則會(huì)采用  extent  區(qū)段的形式,分配一片連續(xù)的塊,inode中只需要記錄開(kāi)始?jí)K號(hào)與長(zhǎng)度即可,不需要索引記錄所有的塊。這除了減輕inode的壓力之外,連續(xù)的塊可以把隨機(jī)寫(xiě)改為順序?qū)?,加快?xiě)性能。連續(xù)的塊也符合  局部性原理,在預(yù)讀時(shí)可以加大命中概率,進(jìn)而加快讀性能。

# mount -o ...delalloc... /home

ext4掛載參數(shù):inode_readahead_blks

ext4從inode表中預(yù)讀的indoe  block最大數(shù)量。訪問(wèn)文件必須經(jīng)過(guò)inode獲取文件信息、數(shù)據(jù)塊地址。如果需要訪問(wèn)的inode都在內(nèi)存中命中,就不需要從磁盤(pán)中讀取,毫無(wú)疑問(wèn)能提高讀性能。其默認(rèn)值是32,表示最大預(yù)讀  32 × block_size 即 64K 的inode數(shù)據(jù),在內(nèi)存充足的情況下,我們毫無(wú)疑問(wèn)可以進(jìn)一步擴(kuò)大,讓其預(yù)讀更多。

# mount -o ...inode_readahead_blks=4096... /home

ext4掛載參數(shù):journal_async_commit

commit塊可以不等待descriptor塊,直接往磁盤(pán)寫(xiě)。這會(huì)加快日志的速度。

# mount -o ...journal_async_commit... /home

ext4掛載參數(shù):commit

ext4一次緩存多少秒的數(shù)據(jù)。默認(rèn)值是5,表示如果此時(shí)掉電,你最多丟失5s的數(shù)據(jù)量。設(shè)置更大的數(shù)據(jù),就可以緩存更多的數(shù)據(jù),相對(duì)的掉電也有可能丟失更多的數(shù)據(jù)。在此服務(wù)器不怕掉電的情況,把數(shù)值加大可以提高性能。

# mount -o ...commit=1000... /home

ext4掛載參數(shù)匯總

最終在不能umount情況下,我執(zhí)行的調(diào)整掛載參數(shù)的命令為:

mount -o remount,rw,noatime,nobarrier,delalloc,inode_readahead_blks=4096,journal_async_commit,commit=1800  /home

此外,在/etc/fstab中也對(duì)應(yīng)修改過(guò)來(lái),避免重啟后優(yōu)化丟失

# cat /etc/fstab UUID=...  /home  ext4  defaults,rw,noatime,nobarrier,delalloc,inode_readahead_blks=4096,journal_async_commit,commit=1800,data=writeback 0 0 ...

頁(yè)緩存

頁(yè)緩存在FS與通用塊層之間,其實(shí)也可以歸到通用塊層中。為了提高IO性能,減少真實(shí)的從磁盤(pán)讀寫(xiě)的次數(shù),Linux內(nèi)核設(shè)計(jì)了一層內(nèi)存緩存,把磁盤(pán)數(shù)據(jù)緩存到內(nèi)存中。由于內(nèi)存以4K大小的  頁(yè) 為單位管理,磁盤(pán)數(shù)據(jù)也以頁(yè)為單位緩存,因此也稱(chēng)為頁(yè)緩存。在每個(gè)緩存頁(yè)中,都包含了部分磁盤(pán)信息的副本。

如果因?yàn)橹白x寫(xiě)過(guò)或者被預(yù)讀加載進(jìn)來(lái),要讀取數(shù)據(jù)剛好在緩存中命中,就可以直接從緩存中讀取,不需要深入到磁盤(pán)。不管是同步寫(xiě)還是異步寫(xiě),都會(huì)把數(shù)據(jù)copy到緩存,差別在于異步寫(xiě)只是copy且把頁(yè)標(biāo)識(shí)臟后直接返回,而同步寫(xiě)還會(huì)調(diào)用類(lèi)似fsync()的操作等待回寫(xiě),詳細(xì)可以看內(nèi)核函數(shù)generic_file_write_iter()。異步寫(xiě)產(chǎn)生的臟數(shù)據(jù)會(huì)在“合適”的時(shí)候被內(nèi)核工作隊(duì)列writeback進(jìn)程回刷。

那么,什么時(shí)候是合適的時(shí)候呢?最多能緩存多少數(shù)據(jù)呢?對(duì)此次優(yōu)化的服務(wù)器而言,毫無(wú)疑問(wèn)延遲回刷可以在頻繁的刪改文件中減少寫(xiě)磁盤(pán)次數(shù),緩存更多的數(shù)據(jù)可以更容易合并隨機(jī)IO請(qǐng)求,有助于提升性能。

在/proc/sys/vm中有以下文件與回刷臟數(shù)據(jù)密切相關(guān):

配置文件功能默認(rèn)值dirty_background_ratio觸發(fā)回刷的臟數(shù)據(jù)占可用內(nèi)存的百分比0dirty_background_bytes觸發(fā)回刷的臟數(shù)據(jù)量10dirty_bytes觸發(fā)同步寫(xiě)的臟數(shù)據(jù)量0dirty_ratio觸發(fā)同步寫(xiě)的臟數(shù)據(jù)占可用內(nèi)存的百分比20dirty_expire_centisecs臟數(shù)據(jù)超時(shí)回刷時(shí)間(單位:1/100s)3000dirty_writeback_centisecs回刷進(jìn)程定時(shí)喚醒時(shí)間(單位:1/100s)500

對(duì)上述的配置文件,有幾點(diǎn)要補(bǔ)充的:

  1. XXX_ratio 和 XXX_bytes 是同一個(gè)配置屬性的不同計(jì)算方法,優(yōu)先級(jí) XXX_bytes > XXX_ratio

  2. 可用內(nèi)存并不是系統(tǒng)所有內(nèi)存,而是free pages + reclaimable pages

  3. 臟數(shù)據(jù)超時(shí)表示內(nèi)存中數(shù)據(jù)標(biāo)識(shí)臟一定時(shí)間后,下次回刷進(jìn)程工作時(shí)就必須回刷

  4. 回刷進(jìn)程既會(huì)定時(shí)喚醒,也會(huì)在臟數(shù)據(jù)過(guò)多時(shí)被動(dòng)喚醒。

dirty_background_XXX與dirty_XXX的差別在于前者只是喚醒回刷進(jìn)程,此時(shí)應(yīng)用依然可以異步寫(xiě)數(shù)據(jù)到Cache,當(dāng)臟數(shù)據(jù)比例繼續(xù)增加,觸發(fā)dirty_XXX的條件,不再支持應(yīng)用異步寫(xiě)。

更完整的功能介紹,可以看內(nèi)核文檔Documentation/sysctl/vm.txt,也可看我寫(xiě)的一篇總結(jié)博客《Linux  臟數(shù)據(jù)回刷參數(shù)與調(diào)優(yōu)》

對(duì)當(dāng)前的案例而言,我的配置如下:

dirty_background_ratio = 60 dirty_ratio = 80 dirty_writeback_centisecs = 6000 dirty_expire_centisecs = 12000

這樣的配置有以下特點(diǎn):

  • 當(dāng)臟數(shù)據(jù)達(dá)到可用內(nèi)存的60%時(shí)喚醒回刷進(jìn)程

  • 當(dāng)臟數(shù)據(jù)達(dá)到可用內(nèi)存的80%時(shí),應(yīng)用每一筆數(shù)據(jù)都必須同步等待

  • 每隔60s喚醒一次回刷進(jìn)程

  • 內(nèi)存中臟數(shù)據(jù)存在時(shí)間超過(guò)120s則在下一次喚醒時(shí)回刷

當(dāng)然,為了避免重啟后丟失優(yōu)化結(jié)果,我們?cè)?etc/sysctl.conf中寫(xiě)入:

# cat /etc/sysctl.conf ... vm.dirty_background_ratio = 60 vm.dirty_ratio = 80 vm.dirty_expire_centisecs = 12000 vm.dirty_writeback_centisecs = 6000

Request層

在異步寫(xiě)的場(chǎng)景中,當(dāng)臟頁(yè)達(dá)到一定比例,就需要通過(guò)通用塊層把頁(yè)緩存里的數(shù)據(jù)回刷到磁盤(pán)中。bio層記錄了磁盤(pán)塊與內(nèi)存頁(yè)之間的關(guān)系,在request層把多個(gè)物理塊連續(xù)的bio合并成一個(gè)request,然后根據(jù)特定的IO調(diào)度算法對(duì)系統(tǒng)內(nèi)所有進(jìn)程產(chǎn)生的IO請(qǐng)求進(jìn)行合并、排序。那么都有什么IO調(diào)度算法呢?

網(wǎng)上檢索IO調(diào)度算法,大量的資料都在描述Deadline,CFQ,NOOP這3種調(diào)度算法,卻沒(méi)有備注這只是單隊(duì)列上適用的調(diào)度算法。在最新的代碼上(我分析的代碼版本為  5.7.0),已經(jīng)完全切換到multi-queue的新架構(gòu)上了,支持的IO調(diào)度算法就成了mq-deadline,BFQ,Kyber,none。

關(guān)于不同IO調(diào)度算法的優(yōu)劣,網(wǎng)上有非常多的資料,本文不再累述。

在《Linux-storage-stack-diagram_v4.10》 對(duì) Block Layer 的描述可以形象闡述單隊(duì)列與多隊(duì)列的差異。

如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)
如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)

單隊(duì)列的架構(gòu),一個(gè)塊設(shè)備只有一個(gè)全局隊(duì)列,所有請(qǐng)求都要往這個(gè)隊(duì)列里面塞,這在多核高并發(fā)的情況下,尤其像服務(wù)器動(dòng)則32個(gè)核的情況下,為了保證互斥而加的鎖就導(dǎo)致了非常大的開(kāi)銷(xiāo)。此外,如果磁盤(pán)支持多隊(duì)列并行處理,單隊(duì)列的模型不能充分發(fā)揮其優(yōu)越的性能。

多隊(duì)列的架構(gòu)下,創(chuàng)建了Software queues和Hardware dispatch queues兩級(jí)隊(duì)列。Software queues是每個(gè)CPU  core一個(gè)隊(duì)列,且在其中實(shí)現(xiàn)IO調(diào)度。由于每個(gè)CPU一個(gè)單獨(dú)隊(duì)列,因此不存在鎖競(jìng)爭(zhēng)問(wèn)題。Hardware Dispatch  Queues的數(shù)量跟硬件情況有關(guān),每個(gè)磁盤(pán)一個(gè)隊(duì)列,如果磁盤(pán)支持并行N個(gè)隊(duì)列,則也會(huì)創(chuàng)建N個(gè)隊(duì)列。在IO請(qǐng)求從Software  queues提交到Hardware Dispatch Queues的過(guò)程中是需要加鎖的。理論上,多隊(duì)列的架構(gòu)的效率最差也只是跟單隊(duì)列架構(gòu)持平。

咱們回到當(dāng)前待優(yōu)化的服務(wù)器,當(dāng)前使用的是什么IO調(diào)度器呢?

# cat /sys/block/vda/queue/scheduler none # cat /sys/block/sda/queue/scheduler noop [deadline] cfq

這服務(wù)器的內(nèi)核版本是

# uname -r 3.13.0-170-generic

查看Linux內(nèi)核git提交記錄,發(fā)現(xiàn)在 3.13.0 的內(nèi)核版本上還沒(méi)有實(shí)現(xiàn)適用于多隊(duì)列的IO調(diào)度算法,且此時(shí)還沒(méi)完全切到多隊(duì)列架構(gòu),因此使用單隊(duì)列的  sda 設(shè)備依然存在傳統(tǒng)的noop,deadline和cfq調(diào)度算法,而使用多隊(duì)列的 vda  設(shè)備(virtio)的IO調(diào)度算法只有none。為了使用mq-deadline調(diào)度算法把內(nèi)核升級(jí)的風(fēng)險(xiǎn)似乎很大。因此IO調(diào)度算法方面沒(méi)太多可優(yōu)化的。

但Request層優(yōu)化只能這樣了?既然IO調(diào)度算法無(wú)法優(yōu)化,我們是否可以修改queue相關(guān)的參數(shù)?例如加大Request隊(duì)列的長(zhǎng)度,加大預(yù)讀的數(shù)據(jù)量。

在/sys/block/vda/queue中有兩個(gè)可寫(xiě)的文件nr_requests和read_ahead_kb,前者是配置塊層最大可以申請(qǐng)的request數(shù)量,后者是預(yù)讀最大的數(shù)據(jù)量。默認(rèn)情況下,

nr_request = 128 read_ahead_kb = 128

我擴(kuò)大為

nr_request = 1024 read_ahead_kb = 512

優(yōu)化效果

優(yōu)化后,在滿(mǎn)負(fù)荷的情況下,查看內(nèi)存使用情況:

# cat /proc/meminfo MemTotal:       49459060 kB MemFree:         1233512 kB Buffers:        12643752 kB Cached:         21447280 kB Active:         19860928 kB Inactive:       16930904 kB Active(anon):    2704008 kB Inactive(anon):    19004 kB Active(file):   17156920 kB Inactive(file): 16911900 kB ... Dirty:           7437540 kB Writeback:          1456 kB

可以看到,文件相關(guān)內(nèi)存(Active(file) + Inactive(file)  )達(dá)到了32.49GB,臟數(shù)據(jù)達(dá)到7.09GB。臟數(shù)據(jù)量比預(yù)期要少,遠(yuǎn)沒(méi)達(dá)到dirty_background_ratio和dirty_ratio設(shè)置的閾值。因此,如果需要緩存更多的寫(xiě)數(shù)據(jù),只能延長(zhǎng)定時(shí)喚醒回刷的時(shí)間dirty_writeback_centisecs。這個(gè)服務(wù)器主要用于編譯SDK,讀的需求遠(yuǎn)大于寫(xiě),因此緩存更多的臟數(shù)據(jù)沒(méi)太大意義。

我還發(fā)現(xiàn)Buffers達(dá)到了12G,應(yīng)該是ext4的inode占用了大量的緩存。如上分析的,此服務(wù)器的ext4有大量富余的inode,在緩存的元數(shù)據(jù)里,無(wú)效的inode不知道占比多少。減少inode數(shù)量,提高inode利用率,說(shuō)不定可以提高inode預(yù)讀的命中率。

優(yōu)化后,一次使能8個(gè)SDK并行編譯,走完一次完整的編譯流程(包括更新代碼,抓取提交,編譯內(nèi)核,編譯SDK等),在沒(méi)有進(jìn)入錯(cuò)誤處理流程的情況下,用時(shí)大概13分鐘。

關(guān)于如何進(jìn)行編譯服務(wù)器性能優(yōu)化實(shí)戰(zhàn)就分享到這里啦,希望上述內(nèi)容能夠讓大家有所提升。如果想要學(xué)習(xí)更多知識(shí),請(qǐng)大家多多留意小編的更新。謝謝大家關(guān)注一下億速云網(wǎng)站!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI