您好,登錄后才能下訂單哦!
原創
2016-09-12
熊軍
【云和恩墨】性能優化:Linux環境下合理配置大內存頁(HugePage)
熊軍(老熊)
云和恩墨西區總經理
Oracle ACED,ACOUG核心會員
PC Server發展到今天,在性能方面有著長足的進步。64位的CPU在數年前都已經進入到尋常的家用PC之中,更別說是更高端的PC Server;在Intel和AMD兩大處理器巨頭的努力下,x86 CPU在處理能力上不斷提升;同時隨著制造工藝的發展,在PC Server上能夠安裝的內存容量也越來越大,現在隨處可見數十G內存的PC Server。正是硬件的發展,使得PC Server的處理能力越來越強大,性能越來越高。而在穩定性方面,搭配PC Server和Linux操作系統,同樣能夠滿重要業務系統所需要的穩定性和可靠性。當然在成本方面,引用一位在行業軟件廠商的網友的話來說,“如果不用PC Server改用小型機,那我們賺什么錢啊?”。不管從初期的購買,運行期的能耗和維護成本,PC Server都比相同處理能力的小型機便宜很多。正是在性能和成本這兩個重要因素的影響下,運行在PC Server上的數據庫越來越多。筆者所服務的一些客戶,甚至把高端的PCServer虛擬化成多臺機器,在每臺虛擬機上跑一套Oracle數據庫,這些數據庫不乏承載著重要的生產系統。
毫無疑問,在PC Server上運行Oracle數據庫,最適合的操作系統無疑是Linux。作為與UNIX極為類似的操作系統,在穩定性、可靠性和性能方面有著與UNIX同樣優異的表現。但是Linux在內存分頁處理機制上與AIX、HP-UX等操作系統相比有一個明顯的缺陷,而這個缺陷在使用較大SGA的Oracle數據庫上體現尤為明顯,嚴重時對數據庫性能有著顯著的負面影響,甚至會導致數據庫完全停止響應。而本文就將從一個案例來詳述這種缺陷,并使用Linux下的大內存頁來解決這一問題。
案例的引入
客戶的一套系統,出現了嚴重的性能問題。在問題出現時,系統基本不可使用,應用上所有的業務操作完全失去響應。系統的數據庫是運行在RHEL 5.2 (Red Hat Enterprise Linux Server release 5 (Tikanga))下的Oracle 10.2.0.4 Oracle Database,CPU為4顆4核至強處理器(Intel(R) Xeon(R) CPU E7430 @ 2.13GHz),也就是邏輯CPU為16,內存32GB。故障期間,數據庫服務器的CPU長期保持在100%。甚至將應用的所有Weblogic Server都關閉之后,數據庫服務器的CPU利用率在數分鐘之內都一直是100%,然后逐漸下降,大約需要經過20分鐘才會下降到正常的空閑狀態,因為這個時候所有的應用都已經關閉,只有非常低的CPU利用率才是正常的狀態。據這套系統的數據庫維護人員反映,這種情況已經出現多次,就算是重啟數據庫之后,過不了一兩天,這樣的故障同樣會出現。同時這套系統最近也沒做過大的變動。
筆者在接到接到故障報告后,通過SSH連接到數據庫數據庫都非常慢,需要差不多1分鐘才能連接上去。先簡單的看一下服務器的性能狀況,發展IO極低、內存剩余還比較多,至少還有1GB以上,也沒有page in / page out。而最顯著的現象就是CPU利用率相當地高,一直保持在100%,同時CPU利用率的SYS部分,均在95%以上。而操作系統運行隊列也一直在200以上。服務器內存的使用情況如下:
從現象上看,SYS CPU高是分析問題的一個重要線索。
在以最快的速度了解了一下操作系統層面的性能情況之后,立即通過Sqlplus連接到數據庫,查看數據庫內部的性能信息:(注:以下數據關于SQL、服務器名稱、數據庫名稱等相關信息經過處理。)
...為節省篇幅,省略部分內容...
調用waitevent,查看等待事件
從數據庫中的活動以及等待事件來看,并沒有太大的異常。值得注意的是, 在數據庫服務器CPU利用率長期在100%,或物理內存耗盡并伴有大量的交換內存換入換出時,需要仔細地診斷數據庫中的性能現象,比如某類較多的等待事件,是由CPU或內存不足導致的結果還是因為這些數據庫中的特定的活動才是Root Cause引起CPU過高或內存耗盡。
從上面的數據來看,活動會話并不是特別多,不到50個,加上后臺進程的數量,與操作系統中高達200的運行相比,存在不小的差異。數據庫中主要有三類的非空閑等待事件,IO相關的等待如db file sequential read,database link相關的SQL*Net more data from dblink以及latch 相關的等待事件。在這三類種,通常只有latch這類等待事件才會引起CPU的利用率增加。
通過分析對比AWR報告,在故障期間和正常期間,從數據庫活動來說,沒有特別明顯的差異。但是在系統統計上,差異較大:
上面的數據中,是來自于包含故障時間段的1小時(1st)和正常時間段1小時(2nd)的AWR的對比數據。對于故障分析來說,特別是故障時間比較短的情況下,1小時的AWR報告會不夠準確地反映故障期間的性能情況。但是我們在Trouble Shooting之時,首要的是需要從各種數據中,確定方向。正如前面提到,SYS部分的CPU利用率過高是一個很重要的線索,而數據庫內部的其他性能數據相差不大的情況下,可以先從CPU這一點著手。
操作系統中CPU使用分析
那么,在操作系統中,SYS和USER這兩個不同的利用率代表著什么?或者說二者有什么區別?
簡單來說,CPU利用率中的SYS部分,指的是操作系統內核(Kernel)使用的CPU部分,也就是運行在內核態的代碼所消耗的CPU,最常見的就是系統調用(SYS CALL)時消耗的CPU。而USER部分則是應用軟件自己的代碼使用的CPU部分,也就是運行在用戶態的代碼所消耗的CPU。比如Oracle在執行SQL時,從磁盤讀數據到db buffer cache,需要發起read調用,這個read調用主要是由操作系統內核包括設備驅動程序的代碼在運行,因此消耗的CPU計算到SYS部分;而Oracle在解析從磁盤中讀到的數據時,則只是Oracle自己的代碼在運行,因此消耗的CPU計算到USER部分。
那么SYS部分的CPU主要會由哪些操作或是系統調用產生呢:
1. I/O操作,比如讀寫文件、訪問外設、通過網絡傳輸數據等。這部分操作一般不會消耗太多的CPU,因為主要的時間消耗會在IO操作的設備上。比如從磁盤讀文件時,主要的時間在磁盤內部的操作上,而消耗的CPU時間只占I/O操作響應時間的少部分。只有在過高的并發I/O時才可能會使SYS CPU有所增加。
2. 內存管理,比如應用進程向操作系統申請內存,操作系統維護系統可用內存,交換空間換頁等。其實與Oracle類似,越大的內存,越頻繁的內存管理操作,CPU的消耗會越高。
3. 進程調度。這部分CPU的使用,在于操作系統中運行隊列的長短,越長的運行隊列,表明越多的進程需要調度,那么內核的負擔也就越高。
4. 其他,包括進程間通信、信號量處理、設備驅動程序內部一些活動等等。
從系統故障時的性能數據來看,內存管理和進程調度這兩項可能是引起SYS CPU很高的原因。但是運行隊列高達200以上,很可能是由于CPU利用率高導致的結果,而不是因為運行隊列高導致了CPU利用率高。從數據庫里面來看活動會話數不是特別高。那么接下來,需要關注是否是由于系統內存管理方面的問題導致了CPU利用率過高?
回顧本文開始部分收集的/proc/meminfo中系統內存方面數據,可以發現一項重要的數據:
PageTables: 4749076 kB
從數據可以看到,PageTables內存達到了 4637MB。PageTables在字面意思上是指“頁面表”。簡單地說,就是操作系統內核用于維護進程線性虛擬地址和實際物理內存地址對應關系的表格。
現代計算機對于物理內存,通常是將其以頁(Page Frame)為單位進行管理和分配,在 x86處理器架構上,頁面大小為4K。運行在操作系統上的進程,可訪問的地址空間稱為虛地址空間,跟處理器位數有關。對于32位的x86處理器,進程的可訪問地址空間為4GB。在操作系統中運行的每一個進程,都有其獨立的虛地址空間或線性地址空間,而這個地址空間同樣也是按頁(Page)進行管理,在Linux中,頁大小通常為4KB。進程在訪問內存時,由操作系統和硬件配合,負責將進程的虛擬地址轉換成為物理地址。兩個不同的進程,其相同的虛擬線性地址,指向的物理內存,可能相同,比如共享內存;也可能不同,比如進程的私有內存。
下圖是關于虛擬地址和物理內存對應關系的示意圖:
假設有兩個進程A、B,分別有一個內存指針指向的地址為0x12345(0x表示16進制數),比如一個進程fork或clone出另一個進程,那么這2個進程就會存在指向相同內存地址的指針的情況。進程在訪問0x12345這個地址指向的內存時,操作系統將這個地址轉換為物理地址,比如A進程為0x23456,B進程為0x34567,二者互不影響。那么這個物理地址是什么時候得來?對于進程私有內存(大部分情況均是如此)來說,是進程在向操作系統請求分配內存時得來。進程向操作系統請求分配內存時,操作系統將空閑的物理內存以Page為單位分配給進程,同時給進程產生一個虛擬線程地址,在虛擬地址和物理內存地址之間建立 映射關系,這個虛擬地址作為結果返回給進程。
Page Table(頁表)就是用于操作系統維護進程虛擬地址和物理內存對應關系的數據結構。下圖是一個比較簡單情況下的Page Table示意圖:
下面簡單地描述在32位系統下,頁大小為4K時,操作系統是如何為進程的虛擬地址和實際物理地址之間進行轉換的。
1. 目錄表是用于索引頁表的數據結構,每個目錄項占32位,即4字節,存儲一個頁表的位置。目錄表剛好占用1頁內存,即4KB,可以存儲1024個目錄項,也就是可以存儲1024個頁表的位置。
2. 頁表項(Page Table Entry)大小為4字節,存儲一個物理內存頁起始地址。每個頁表同樣占用4K內存,可以存儲1024個物理內存頁起始地址。由于物理內存頁起始地址以4KB為單位對齊,所以32位中,只需要20位來表示地址,其他12位用于其他用途,比如表示這1內存頁是只讀還是可寫等等。
3. 1024個頁表,每個頁表1024個物理內存頁起始地址,合計就是1M個地址,每個地址指向的物理內存頁大小為4KB,合計為4GB。
4. 操作系統及硬件將虛擬地址映射為物理地址時,將虛擬地址的31-22這10位用于從目錄項中索引到1024個頁表中的一個;將虛擬地址的12-21這10位用于從頁表中索引到1024個頁表項中的一個。從這個索引到的頁表項中得到物理內存頁的起始地址,然后將虛擬地址的0-11這12位用作4KB內存頁中的偏移量。那么物理內存頁起始地址加上偏移量就是進程所需要訪問的物理內存的地址。
再看看目錄表和頁表這2種數據結構占用的空間會有多少。目錄表固定只有4KB。而頁表呢?由于最多有1024個頁表,每個頁表占用4KB,因此頁表最多占用4MB內存。實際上32位Linux中的進程通常不會那么大的頁表。進程不可能用完所有的4GB大小地址空間,甚至有1GB虛擬地址空間分給了內核。同時Linux不會為進程一次性建立那么大的頁表,只有進程在分配和訪問內存時,操作系統才會為進程建立相應地址的映射。
這里只描述了最簡單情況下的分頁映射。實際上頁表目錄連同頁表一共有四級。同時在32位下啟用PAE或64位系統,其頁表結構比上面的示意圖更復雜。但無論怎么樣,最后一級即頁表的結構是一致的。在64位系統中,Page Table(頁表)中的頁表項,與32位相比,大小從32位變為64位。那么這會有多大的影響?假如一個進程,訪問的物理內存有1GB,即262144個內存頁,在32位系統中,頁表需要262144*4/1024/1024=1MB,而在64位系統下,頁表占用的空間增加1倍,即為2MB。那再看看對于Linux系統中運行的Oracle數據庫,又是怎么樣一番情景。本文案例中數據庫的SGA大小12GB,如果一個OracleProcess訪問到了所有的SGA內存,那么其頁表大小會是24MB,這是一個驚人的數字。這里忽略掉PGA,因為平均下來每個進程的PGA不超過2M,與SGA相比實在太小。從AWR報告來看,有300個左右的會話,那么這300個連接的頁表會達到7200MB,只不過并不是每個進程都會訪問到SGA中所有的內存。而從meminfo查看到的Page Tables大小達到4637MB,這么大的Page Table空間,正是300個會話,SGA大小達到12GB的結果。
系統中顯然不會只有Page Table這唯一的內存管理數據結構,還有其他一些數據結構用于管理內存。這些過大的內存管理結構,無疑會大大增加操作系統內核的負擔和對CPU的消耗。而在負載變化或其他原因導致內存需求大幅變化,比如多進程同時申請大量的內存,可能引起CPU在短時間內達到高峰,從而引起問題。
使用大內存頁來解決問題
雖然沒有確實的證據,也沒有足夠長的時間來收集足夠的證據來證明是過大的Page Table導致了問題,那需要面臨多次半小時以上的系統不可用故障。但是從目前來看,這是最大的可疑點。因此,決定先使用大內存頁來調優系統的內存使用。
大內存頁是一種統稱,在低版本的Linux中為Large Page,而當前主流的Linux版本中為Huge Page。下面以Huge Page為例來說明Huge Page的優點及如何使用。
使用大內存頁有哪些好處:
1. 減少頁表(Page Table)大小。每一個Huge Page,對應的是連續的2MB物理內存,這樣12GB的物理內存只需要48KB的Page Table,與原來的24MB相比減少很多。
2. Huge Page內存只能鎖定在物理內存中,不能被交換到交換區。這樣避免了交換引起的性能影響。
3. 由于頁表數量的減少,使得CPU中的TLB(可理解為CPU對頁表的CACHE)的命中率大大提高。
4. 針對Huge Page的頁表,在各進程之間可以共享,也降低了Page Table的大小。實際上這里可以反映出Linux在分頁處理機制上的缺陷。而其他操作系統,比如AIX,對于共享內存段這樣的內存,進程共享相同的頁表,避免了Linux的這種問題。像筆者維護的一套系統,連接數平常都是5000以上,實例的SGA在60GB左右,要是按Linux的分頁處理方式,系統中大部分內存都會被頁表給用掉。
那么,怎么樣為Oracle啟用大內存頁(Huge Page)?以下是實施步驟。由于案例中涉及的數據庫在過一段時間后將SGA調整為了18G,這里就以18G為例:
1. 檢查/proc/meminfo,確認系統支持HugePage:
HugePages Total表示系統中配置的大內存頁頁面數。HugePages Free表示沒有訪問過的大內存頁面數,這里free容易引起誤解,這在稍后有所解釋。HugePages Rsvd表示已經分配但是還未使用的頁面數。Hugepagesize表示大內存頁面大小,這里為2MB,注意在有的內核配置中可能為4MB。
比如HugePages總計11GB,SGA_MAX_SIZE為10GB,SGA_TARGET為8GB。那么數據庫啟動后,會根據SGA_MAX_SIZE分配HugePage內存,這里為10GB,真正Free的HugePage內存為11-10=1G。但是SGA_TARGET只有8GB,那么會有2GB不會被訪問到,則HugePage_Free為2+1=3GB,HugePage_Rsvd內存有2GB。這里實際上可以給其他實例使用的只有1GB,也就是真正意義上的Free只有1GB。
1. 計劃要設置的內存頁數量。到目前為止,大內存頁只能用于共享內存段等少量類型 的內存。一旦將物理內存用作大內存頁,那么這些物理內存就不能用作其他用途,比如作為進程的私有內存。因此不能將過多的內存設置為大內存頁。我們通常將大內存頁用作Oracle數據庫的SGA,那么大內存頁數量:
HugePages_Total=ceil(SGA_MAX_SIZE/Hugepagesize)+N
比如,為數據庫設置的SGA_MAX_SIZE為18GB,那么頁面數可以為ceil(18*1024/2)+2=9218。這里加上N,是需要將HugePage內存空間設置得比SGA_MAX_SIZE稍大,通常為1-2即可。我們通過ipcs -m命令查看共享內存段的大小,可以看到共享內存段的大小實際上比SGA_MAX_SIZE約大。如果服務器上有多個Oracle實例,需要為每個實例考慮共享內存段多出的部分,即N值會越大。另外,Oracle數據庫要么全部使用大內存頁,要么完全不使用大內存頁,因此不合適的HugePages_Total將造成內存的浪費。
除了使用SGA_MAX_SIZE計算,也可以通過ipcs -m所獲取的共享內存段大小計算出更準確的HugePages_Total。
HugePages_Total=sum(ceil(share_segment_size/Hugepagesize))
2. 修改/etc/sysctl.conf文件,增加如下行:
vm.nr_hugepages=9218
然后執行sysctl –p命令,使配置生效。
這里vm.nr_hugepages這個參數值為第2步計算出的大內存頁數量。然后檢查/proc/meminfo,如果HugePages_Total小于設置的數量,那么表明沒有足夠的連續物理內存用于這些大內存頁,需要重啟服務器。
3. 在/etc/security/limits.conf文件中增加如下行:
oracle soft memlock 18878464
oracle hard memlock 18878464
這里設定oracle用戶可以鎖定內存的大小 ,以KB為單位。
然后重新以oracle用戶連接到數據庫服務器,使用ulimit -a命令,可以看到:
max lockedmemory (kbytes, -l) 18878464
這里將memlock配置為unlimited也可以。
4. 如果數據庫使用MANUAL方式管理SGA,需要改為AUTO方式,即將SGA_TARGET_SIZE設置為大于0的值。對于11g,由于HugePage只能用于共享內存,不能用于PGA,所以不能使用AMM,即不能設置MEMORY_TARGET為大于0,只能分別設置SGA和PGA,SGA同樣只能是AUTO方式管理。
5. 最后啟動數據庫,檢查/proc/meminfo中查看HugePages_Free是否已經減少。如果已經減少,表明已經使用到HugePage Memory。不過查看出故障數據庫服務器上的/proc/meminfo時發現,居然沒有HugePage相關的信息,sysctl -a查看所有系統參數也沒有找到vm.nr_hugepages這個參數。這是由于Linux內核沒有編譯進HugePage這個特性。我們需要使用其他的內核來啟用HugePage。
查看/boot/grub/grub.conf:
發現這個系統使用的內核帶有"xen"字樣,我們修改這個文件,將default=0改為default=2,或者將前面2種內核用#號屏蔽掉,然后重啟數據庫服務器,發現新的內核已經支持HugePage。
數據庫啟用大內存頁之后,本文描述的性能問題甚至是在增大了SGA的情況下也沒有出現。觀察/proc/meminfo數據,PageTables占用的內存一直保持在120M以下,與原來相比,減少了4500MB。據觀察,CPU的利用率也較使用HugePages之前有所下降,而系統運行也相當地穩定,至少沒有出現因使用HugePage而產生的BUG。
測試表明,對于OLTP系統來說,在運行Oracle數據庫的Linux上啟用HugePage,數據庫處理能力和響應時間均有不同程度的提高,最高甚至可以達到10%以上。
總結
本文以一個案例,介紹了Linux操作系統下大內存頁在性能提升方面的作用,以及如何設置相應的參數來啟用大內存頁。在本文最后,筆者建議在Linux操作系統中運行Oracle數據庫時,啟用大內存頁來避免本文案例遇到的性能問題,或進一步提高系統性能。可以說,HugePage是少數不需要額外代價就能提升性能的特性。另外值得高興的是,新版本的Linux內核提供了Transparent Huge Pages,以便運行在Linux上的應用能更廣泛更方便地使用大內存頁,而不僅僅是只有共享內存這類內存才能使用大內存頁。對于這一特性引起的變化,讓我們拭目以待。
--the end
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。