性能優化:Linux環境下合理配置大內存頁(HugePage)

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上的應用能更廣泛更方便地使用大內存頁,而不僅僅是隻有共享內存這類內存才能使用大內存頁。對於這一特性引起的變化,讓我們拭目以待。

轉自:https://www.cnblogs.com/lhrbest/p/5866284.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章