1. 程式人生 > 其它 >Mongo伺服器管理之部署MongoDB討論

Mongo伺服器管理之部署MongoDB討論

  雖然現在是雲時代,很少會接觸伺服器底層硬體,但是本章將會就部署生產伺服器給出相關建議。也可以更好的瞭解底層。具體來講,包括以下幾方面:

    • 選購硬體、挑選設定方法;
    • 使用虛擬化環境;
    • 重要的核心與磁碟10設定;
    • 網路設定:哪些元件之間需要建立連線。

1.設計系統結構

  通常,我們會希望對系統進行優化,以保證資料安全和存取速度。本節將探討在選 擇磁碟、RAID (磁碟陣列)配置、CPU等硬體以及基本軟體元件的過程中,達成以上目標的最佳方法。

1.1 選擇儲存介質

  如果只考慮效能,可按照以下順序選擇介質,從而進行資料存取:

    • 記憶體;
    • 固態磁碟;
    • 機械磁碟。

  可惜,大多情況下,由於預算有限或資料過多,無法將所有資料存入記憶體,而固態磁碟又過於昂貴。因此,標準的部署方案是使用較少的記憶體空間(具體大小取決幹總資料大小)和較大的機械磁碟空間。這種情況下需注意,工作集大小應小於記憶體容量,同時應做好在工作集增長時進行裝置擴充套件的準備。

  如果沒有經費限制,那就去購買更多的記憶體或固態磁碟。

  從記憶體中讀取資料需幾納秒的時間(比如100納秒)。相反地,從磁碟中讀取資料需幾毫秒的時間(比如10毫秒)。單看這兩個數字很難想像出二者間的差距,但如果我們將它們按比例放大就會明白:如果訪問記憶體耗時1秒鐘,則訪問磁碟需耗時超過1天的時間!

  100 納秒 X 10 000 000 = 1 秒

  10 毫秒 x 10 000 000 = 1.16 天

  這些只是近似的計算(磁碟可能略快或記憶體略慢),但差距的大小不會有太大差別。 所以我們會想要儘量少地訪問磁碟。

  即使是更快的機械磁碟,也不會使磁碟讀取時間縮短太多,所以沒有必要花太多錢在這種磁碟上。更多的記憶體或固態磁碟效果會更好。

  一個示例

  圖23-1至圖23-6展示了固態磁碟的優勢。這些圖片中顯示的,是一個在8月8日中午上線的新分片的情況。開始時僅在機械磁碟上部署了一個分片,隨後又在固態磁碟上部署了一個新的分片,接下來兩個分片同時執行。

  如圖23-1所示,機械磁碟的效能峰值可接近每秒5000次査詢,但一般情況下只能做到每秒幾百次查詢。

  作為對照,圖23-2中的圖表顯示了在固態磁碟上進行查詢的狀況。固態磁碟的效能可保持每秒處理5000次請求,峰值則可達到每秒30000次!這一新的分片完全可以獨立承擔整個叢集的工作。

  有關機械磁碟和固態磁碟的對比中,另一點值得注意的是頻繁的磁碟訪問對系統的壓力大小。在使用機械磁碟的伺服器上,我們可從其硬體監控資訊(圖23-3)中看到,磁碟工作十分繁忙。圖中位於上部的曲線表示IO延遲,即CPU等待磁碟IO的時間所佔總時間的百分比。可以看到該百分比至少為10%,高峰時常達到50%以上。這意味著磁碟成為了限制性能的短板(所以此人新添了固態磁碟)。

  作為對比,圖23-4顯示了使用固態磁碟的機器上CPU的使用情況。圖中甚至已經看不出IO延遲的痕跡,上下兩條明顯的曲線分別表示系統時間(system time)和使用者時間(user time)。因此,限制這一機器效能的短板就是CPU的執行速度。圖中曲線超過了 100%,這也說明系統利用了多個處理器核心。將其與圖23-3進行對比可發現,之前的機器由於磁碟IO速度過慢,導致得到充分利用的處理器核心甚至還不足一個。

  最後,在有關鎖時間的圖23-5中可以看到其對MongoDB的影響。在機械磁碟上,資料庫10%到25%的時間處在鎖狀態,有時峰值甚至會達到100%。

  與圖23-6中使用固態磁碟機器上的鎖比例進行比較。MongoDB基本上一直保持非鎖定狀態。(曲線開始部分的凸起是在加上固態磁碟之前的資料讀取操作造成的。)

  可以看到,固態磁碟可以承擔比機械磁碟多得多的工作,但不幸的是,它們無法被大量部署。如果能夠使用它們,那就用吧。就算不可能在整個叢集中使用固態磁碟,也應考慮儘量多得部署,然後使用之前提到的強制熱點資料模型進行優化。

  注意:通常我們不能向已有的副本集中新增固態磁碟(如副本集中存在機械磁碟的話)。如果使用固態磁碟的機器成為主成員(primary member),並接管處理它所能處理的一切工作,則其他成員受速度所限,無法及時複製資料,從而被落在後面。因此,如果要引入固態磁碟的話,向叢集中增加一個新的分片不失為一種更好的方法。

  注意:固態磁碟對幹處理普通資料而言表現優異,但實際上機械磁碟完全可以用於記錄日誌(journal)。用機械磁碟來記錄日誌,而用固態磁碟記錄資料,這樣既能節省固態磁碟的空間,也不會影響效能。

1.2 推薦的RAID配置

  RAID (Redundant Array of Independent Disk,獨立磁碟冗餘陣列,舊稱 Redundant Array of Inexpensive Disk,廉價磁碟冗餘陣列)是一種可以讓我們把多塊磁碟當作單獨一塊磁碟來使用的技術。可使用它來提高磁碟的可靠性或效能,或二者兼有。一組使用RAID技術的磁碟被稱作RAID磁碟陣列。

  RAID根據效能的不同,存在著多種配置方式,通常兼顧了速度與容錯性。下列是幾種最常見的配置方式。

  • RAID0

  使用磁碟分割技術(disk striping)將多個磁碟並列起來以提升效能。每塊磁碟儲存一部分資料,與MongoDB中的分片類似。由於存在多個底層磁碟,因此大量資料可在同一時間寫人磁碟內。這一方式可提高寫入效率。然而,如果其中一塊磁碟發生故障導致資料丟失,則這些資料不會存在備份。這也會導致讀取速度變慢(尤其是在Amazon的Elastic Block Store服務上),因為一些資料卷可能比另一些要慢。

  • RAID1

  使用映象來提高可靠性。同樣的資料副本會被寫入到陣列的每一個成員當中。這一方法的效能要比RAID0低,因為陣列中一個速度慢的成員會拖慢整個陣列的寫入速度。然而,如果其中一塊磁碟發生故障,還可以在陣列中的其他成員上找到資料副本。

  • RAID5

  在使用磁碟分割技術的基礎上,額外儲存資料的校驗資訊,以防伺服器故障導致資料丟失。一般情況下,在一塊磁碟發生故障時RAID5可以自動處理它,使用者並不會感覺到故障的發生。然而,這也使得RAID5成為這些RAID配置方案中最慢的一種,因為它需要在寫入資料時計算校驗資訊。而MongoDB所進行的恰恰是典型的多次少量的資料寫入工作,因此使用RAID5所帶來的代價尤為可觀。

  • RAID 10

  RAID10是一種RAID0和RAID1的組合:資料被分割以提升速度,又被複制映象以提高可靠性。

  推薦使用RAID10,它比RAID0更安全,也能解決RAID1的效能問題。有人覺得在副本集的基礎上再使用RAID1有些浪費,從而選擇RAID0。這是個人喜好問題:你原意為了效能承擔多大的風險呢?

  不要使用RAID5,它非常非常慢。

1.3 CPU

  MongoDB對於CPU的負載很輕(注意在圖23-3和圖23-4中:兩個CPU的處理能力即可滿足每秒10 000次査詢)。如需在記憶體和CPU間選擇一個進行硬體投資,一定要選擇記憶體。理論上來講,在進行讀取或在記憶體中進行排序時,會耗盡多核的運算資源,但在實踐中這種情況很少發生。在建立索引和進行MapReduce ( —個用於大規模資料集並行運算的軟體架構)運算時,對CPU的負載很大,但直到本書寫作之時,增加處理器核數仍無法對這兩種操作起到優化作用。

  如需在速度和核數間做出選擇,應選擇前者。相比更多的並行運算,MongoDB能更好地利用單處理器上的更多週期進行運算。

1.4 選擇作業系統

  64位Linux作業系統是執行MongoDB的最好選擇。可能的話應選擇它作為核心系統。CentOS和RedHat企業版可能是最普遍的選擇,其他的發行版也應能夠執行MongoDB (Ubuntu和Amazon Linux也很常用)。應使用最新發布的穩定版本,因為老舊的、存在缺陷的軟體包或核心有時會產生問題。

  64位Windows系統也能很好地執行MongoDB。

  MongoDB對於其他版本Unix系統的支援並沒有那麼好:如果使用Solaris或者基於BSD的系統,那麼應該小心,因為這些系統釋出的MongoDB,都存在(至少曾經存在)很多問題。

  關於跨平臺相容,有一點需特別注意:MongoDB在所有系統中使用同樣的線路協議(wire protocol),對於資料檔案中的內容也使用同樣的格式進行儲存,所以我們可以基於不同系統的組合來部署MongoDB。例如,可在Windows系統上執行mongos程序,而在Linux上執行mongods來作為其分片。也可在Windows和Linux間複製資料檔案,而不必考慮跨平臺相容的問題。

  如伺服器需處理大量資料,則不要使用32位系統,因為這會限制我們最多隻能處理2 GB的資料(這是由於MongoDB使用記憶體對映的檔案)。副本集的仲裁器和mongos程序可以執行在32位機器上。不要在32位機器上執行其他型別的MongoDB 服務。

  MongoDB只支援小端(little-endian,即儲存二進位制內容時,數字的低位組置於最前面)系統結構。大部分驅動都支援小端和大端(big-endian)兩種系統結構,因此客戶端在兩種系統中均可執行。然而,伺服器只能執行在小端結構的機器上。

1.5 交換空間

  應分配一小塊交換空間,以防系統記憶體使用過多,從而導致核心終止MongoDB的執行。然而,MongoDB通常並不會使用任何交換空間。

  MongoDB所使用的大部分記憶體都是“不穩定的”:只要系統因某些原因而請求記憶體空間,這部分記憶體中的內容就會被重新整理到磁碟中,然後原記憶體則被替換成其他內容。因此,資料庫資料絕不應該被寫入交換空間,因為它首先會被重新整理回磁碟。

  然而,MongoDB在需要對資料進行排序,即建立索引或進行排序操作時,會使用交換空間。在進行此類操作時,MongoDB會盡量不去使用過多記憶體,但如果同時進行很多這種操作,最終就會使用到交換空間。

  如果應用程式在伺服器上用到了交換空間,則應想辦法重新設計應用程式,或者減少那臺伺服器上的負載。

1.6 檔案系統

  在Linux系統上,推薦使用ext4或XFS檔案系統作為資料卷。具有一個能夠在備份時進行檔案系統快照(filesystem snapshot)的檔案系統是不錯的,但是會影響到效能。

  不推薦使用ext3檔案系統,因為它在對資料檔案進行預分配時耗時過長。MongoDB會定期分配2 GB大小的資料檔案並將其內容填充為0。在ext3檔案系統上,這一操作會造成幾分鐘的卡頓。如果一定要使用ext3檔案系統,有幾個相關的優化措施可供選擇。不過如果可以的話,還是應儘量使用其他檔案系統。

  在Windows系統上,使用NTFS和FAT檔案系統都是可以的。

  提示:不要直接使用被掛載的NFS檔案系統作為MongoDB的儲存區域。有些版本的客戶端會隱瞞資料重新整理的真實情況,隨機重新掛載和重新整理頁面快取(page cache),且不支援排他檔案鎖定(exclusive file lock)。使用NFS檔案系統會造成日誌(journar)內容損壞,因此應儘量避免使用。

2.虛擬化

  運用虛擬化(virtualization)技術可方便地使用廉價的硬體來部署系統,並且能夠迅速做出擴充套件。然而,虛擬化也存在缺點,尤其是無法預知的網路和磁碟IO狀況。本節將探討有關虛擬化的具體問題。

2.1 禁止記憶體過度分配

  記憶體過度分配(memory overcommitting)的設定值決定了當程序向作業系統請求過多記憶體時應採取的策略。基於這一設定,核心可能會為程序分配記憶體,哪怕那些記憶體當前是不可用的(期望的結果是,當程序用到這段記憶體時它已變為可用的)。這種核心向程序許諾不存在的記憶體的行為,就叫做記憶體過度分配。這一特性使得MongoDB無法很好地運作。

  vm.overcommit_memeory的值可能為0 (讓核心來猜測過度分配的大小),可能為1(滿足所有記憶體分配請求),也可能為2 (分配的虛擬地址空間最多不超過交換空間與一小部分過度分配的和)。將此值設為2所代表的意義最為複雜,同時也是最佳選擇。執行以下命令將此值設為2:

$ echo 2 > /proc/sys/vm/overcommit_memory

  更改這一設定後無需重啟MongoDB。

2.2 神祕的記憶體

  有時虛擬層無法正確地配備記憶體。因此,一臺虛擬機器號稱擁有100 GB可用記憶體,但可能只能使用其中的60GB。相反,我們曾經發現應該只能使用20GB記憶體的使用者,卻可以將100 GB的資料集全部存入記憶體!

  沒這麼幸運也無所謂。如果預讀大小設定合理,而虛擬機器就是無法使用全部記憶體,這時切換虛擬機器即可。

2.3 處理網路磁碟的IO問題

  磁碟速度的越發緩慢是使用虛擬化技術的最大問題之一。我們通常要和其他使用者共享磁碟,由於每個人都在爭奪磁碟io,因此這加劇了磁碟的效能負擔。也正因為此,虛擬磁碟的效能無法預知:當其他使用者並不頻繁使用磁碟時,磁碟可以工作地很好,而一旦其他人開始壓榨磁碟時,其效能就會迅速下降。

  另一個問題是,儲存裝置時與MongoDB執行的機器間常常並不存在物理上的連線,所以即使磁碟僅供自己使用,依然會比本地磁碟速度慢。這也可能(雖然可能性不大)導致MongoDB伺服器與資料間失去了網路連線。

  Amazon擁有可能是最常用的網格儲存服務,稱為EBS (Elastic Block Store,彈性塊儲存)。EBS中的卷可連線到EC2 (Elastic Compute Cloud,彈性雲端計算)例項,並立即為機器提供近乎任意數量的磁碟空間。從積極的一面來看,這使得備份變得非常簡單(在備份節點上製作快照,掛載EBS驅動到另一個例項上,啟動mongod)。但另一方面,效能的起伏會非常明顯。

  如希望提高效能的可預測性,有以下幾個選項。要保證系統性能和期望中的一樣,最直接的做法是不要將MongoDB託管在雲端。將其託管在自己的伺服器上可以保證效能不會被其他使用者拖慢。不過,許多人不會選擇這種做法。於是,僅次於前一種選項的就是選擇能夠保證一定數量IOPS (IO Operations Per Second,每秒IO操作)的例項。可訪問http://docs.mongodb.org,査看最新的推薦託管服務。

  如果這些選項都無法實現,而一個高負載的EBS卷所提供的磁碟IO又無法滿足需求,那麼可以使些手段。

  基本上,我們能做的就是監視MongoDB所使用的卷。一旦某個卷的速度變慢,立即終止這一例項的執行,接著啟動一個使用另一資料卷的新例項。

  可對以下資料進行監視。

    • IO利用率的峰值(MMS中的“IO延遲”),原因顯而易見。
    • 頁缺失(page faults)發生頻率的峰值。注意,應用程式本身的行為變化也會造成工作集的變化:在部署新版本的應用程式前,應先禁用這一不斷結束並切換例項的指令碼。
    • TCP丟包數的增長情況(在Amazon的服務上這一點尤其嚴重:當效能開始下降時,會頻繁發生TCP丟包的情況)。
    • MongoDB讀寫佇列的峰值(該資料可在MMS或mongo stat的qr/qw列中找到)。

  如果負載發生週期性變化,應確保指令碼考慮了計劃任務的情況,以免其在工作格外繁忙的星期一早上,由於執行計劃任務造成的影響而終止所有例項的執行。

  在使用這些手段之前,應保證對資料留有備份,或存在可與其進行同步的資料集。如果讓每個例項都儲存上TB的資料,我們可能會希望尋找替代方法。另外,該方法不一定有效,如果新分捲上的負荷也很大,則會和原來一樣慢。

2.4 使用非網路磁碟

  本節中使用了一些Amazon服務中特有的詞彙。然而,它也可能適用於其他提供商。

  臨時驅動器(ephemeral drive)是真正和虛擬機器(VM)所在的機器間存在物理連線的磁碟,所以並不存在很多網路儲存中出現的問題。本地磁碟依然可能由於同一個盒子(box)中其他使用者的使用而超過負載,但通過使用更大的盒子可基本確保不會與特別多的使用者共享磁碟。即使是一個稍小的例項,只要其他使用者沒有造成大量的IOPS,臨時驅動器就能經常提供比網路驅動器更好的效能。

  它的缺點從名字上就可以看出來:這些磁碟是臨時的。如果EC2例項停止執行,則無法保證重新啟動例項後還能在同一個盒子裡,資料也隨之不見了。

  因此,應小心使用臨時驅動器。應確保不要將任何重要的,或者沒有備份的資料存放到這些磁盤裡。尤其不要把日記資訊存放在這些臨時磁盤裡,或是網路另一端的資料庫裡。通常來講,應將臨時驅動器當作一個速度稍慢的快取來使用,而非一塊速度快的磁碟。

3.系統配置

  以下幾個系統設定可使MongoDB的執行更加穩定,且主要與磁碟和記憶體的訪問有關。本節將具體學習這些選項及其調整方法。

3.1 禁用 NUMA

  當機器中只有一個CPU時,所有記憶體的存取時間(access time)基本相同。當機器中開始有更多的處理器時,工程師們發現,與其將所有記憶體與CPU的距離保持相同(如圖23-7所示),不如為毎個CPU都設定一些距其更近、訪問速度更快的記憶體,這樣做的效率會更高。

  這種每個CPU都具有自己“本地”記憶體的結構,叫做NUMA (Non-uniform Memory Architecture,非一致記憶體結構),如圖23-8所示。

  對於很多應用程式,NUMA都能夠很好地運作:不同的處理器執行不同的程式,因此通常需要不同的資料。然而,這一結構面對資料庫,尤其是MongoDB時,則表現非常糟糕,這是因為資料庫訪問記憶體的模式與其他應用程式不同。MongoDB需要使用大量記憶體,同時需要CPU能夠訪問其他CPU的“本地記憶體”。然而,很多系統上預設的NUMA設定很難滿足這一需求。

  CPU傾向於優先使用自身的“本地記憶體”,而程序則傾向於優先使用同一CPU。這意味著記憶體通常不會被平均地佔用,結果就是一個處理器使用了其100%的“本地記憶體”,而其他處理器只使用了其一小部分記憶體,如圖23-9所示。

  在圖23-9的情況下,假設CPU1需要一些記憶體中沒有的資料。此時必須使用其“本地記憶體”來存放這些還沒有被讀進記憶體的資料,但其“本地記憶體”已經滿了。於是“本地記憶體”中的一些資料就會被移除出去以騰出空間,哪怕CPU2的“本地記憶體”中還有足夠的空間。這一過程使得MongoDB的執行速度要比期望中慢得多,因為只有一小部分記憶體得到了有效地利用。MongoDB傾向於訪問更多的資料,哪怕效率稍低,而非高效地訪問一小部分資料。

  禁用NUMA是一個能夠提升效能的魔法按鈕,一定要按下它。就像使用固態磁碟一樣,禁用NUMA可提升所有事物的效能。

  如果可能的話,應通過BIOS來禁用NUMA。例如,如果在使用grub,可在grub.cfg中新增numa=off選項:

kernel /boot/vmlinuz-2.6.38-8-generic root=/dev/sda ro quiet numa=off

  如果系統無法在BIOS中禁用NUMA,則可在啟動mongod時使用以下選項:

$ numactl --interleave=all mongod [options]

  將這一命令新增到所有使用的初始化指令碼中。

  此外,禁用zone_reclaim_mode選項。可把該選項認定為“超級NUMA”。該選項被啟用後,CPU訪問一頁記憶體時,該頁記憶體就會被移動到此CPU的“本地記憶體”中。於是,如果一個CPU上的threadA和另一 CPU上的threadB同時訪問一頁記憶體,則每次訪問時,該頁記憶體都會被從一個CPU的“本地記憶體”複製到另一CPU的“本地記憶體“中。這會非常、非常得慢。

  要禁用zone_reclaim_mode,可執行

$ echo 0 > /proc/sys/vm/zone_reclaim_mode

  無需重啟mongod, zone_reclaim_mode選項即可生效。

  啟用NUMA後,主機在MMS上會被顯示成黃色,如圖23-10所示。可通過“Last Ping”選項卡,檢視使其變成黃色的具體警告資訊。圖23-11顯示的警告資訊可說明NUMA是否啟用。

  如果禁用NUMA,那麼MMS上的主機會重新顯示為藍色。(主機顯示為黃色也可能是由於其他原因。應同時査看其他啟動警告資訊。)

3.2 更智慧地預讀取資料

  預讀(readahead)是一種優化手段,即作業系統從磁碟中讀取比實際請求更多的資料。這一優化基於的原理是:計算機所處理的大部分工作都是連續的,即如果載入了一個視訊檔案的前20MB內容,則接下來很可能需要用到緊隨其後的若干MB內容。於是,系統會從磁碟中讀取比實際請求更多的內容,並將其存放到記憶體中,以便隨後的呼叫。

  然而,MongoDB並非是典型的工作負載,設定預讀也是MongoDB系統中的常見問題。MongoDB傾向於從磁碟中隨機讀取很多小塊的資料,所以預設的系統設定並不能很好地運作。如果預讀內容過多,記憶體中會逐漸充滿MongoDB沒有請求的內容,迫使MongoDB更多地訪問磁碟。

  例如,如果希望從磁碟中讀取一個扇區(512位元組)的內容,則磁碟控制器實際上可能會讀取256個扇區,因為它假設我們接下來總會用到這些內容。然而,如果完全隨機地訪問磁碟資料,則這些預讀的扇區都會被浪費掉。如果記憶體中包含了工作集,則其中的255個扇區會被從記憶體中移除,從而存放這些不會用到的內容。事實上256個扇區是很小的預讀數量,有些系統會預設預讀上千扇區的內容。

  幸好,有一種很簡單的方法,可供檢視預讀設定是否已帶來麻煩:檢査MongoDB 駐留集(resident set)的大小,並與系統的總記憶體容量進行比較。

  假設記憶體容量小於資料大小,MongoDB的駐留集大小應稍小於總記憶體大小(例如,如果有50 GB的記憶體,MongoDB應占用了至少46 GB)。如駐留集過小,則說明預讀的內容可能太多了。

  比較駐留集和總記憶體大小這一方法所基於的原理是:被預讀的資料在記憶體中,而 MongoDB沒有請求這些資料,因此不會被計算在MongoDB的常駐記憶體大小中。

  使用blockdev命令,可檢視當前的預讀設定:

$sudo blockdev --report
R0 RA SSZ BSZ    StartSec  Size                  Device
rw 256 512 4096 0          80026361856         /dev/sda
rw 256 512 4096 2048        80025223168         /dev/sda1
rw 256 512 4096 0          2000398934016        /dev/sdb
rw 256 512 1024 2048        98566144            /dev/sdb1
rw 256 512 4096 194560      7999586304          /dev/sdb2
rw 256 512 4096 15818752    19999490048         /dev/sdb3
rw 256 512 4096 54880256    1972300152832        /dev/sdb4

  這裡顯示了每個塊裝置的配置。RA列表示預讀大小,其單位是大小為512位元組的扇區數量。因此,該系統中每個裝置的預讀大小都設定為128 KB (512位元組/扇區x 256個扇區)。

  可使用以下命令,並通過- -setra選項來更改這一設定值:

$ sudo blockdev --setra 16 /dev/sdb3

  那麼,預讀大小設為多少為好呢?推薦數值是16到256之間。預讀大小也不應設得過小,否則讀取一個單獨的文件則需多次訪問磁碟。如文件較大(大於1MB),則應考慮預讀更多的內容。如文件較小,預讀的數值則應小一些,例如32。即使文件非常小,也不要將預讀大小的值設為16以下,這會導致讀取索引資訊時效率低下(索引桶(index bucket)的大小為8 KB)。

  使用RAID時,RAID控制器和組成RAID的每個分捲上都應對預讀進行設定。

  需重啟MongoDB才能使預讀設定生效,這一點看起來有些奇怪。更改磁碟屬性設定難道不應該立即對所有正在執行的程式生效嗎?但可惜,程序會在啟動時複製一份預讀大小的設定值,並一直按照該值運作,直到程序停止執行。

3.3 禁用大記憶體頁面

  啟用大記憶體頁面(hugepage)導致的問題和預讀過多內容導致的問題類似。不要啟用這一特性,除非:

    • 所有資料都存放在記憶體中;
    • 不考慮資料大小不斷增長最終超過記憶體容量的情況。

  MongoDB需載入數量眾多的小塊記憶體,所以啟用大頁面會導致更多的磁碟IO。

  系統以頁面為單位在磁碟和記憶體間轉移資料。頁面大小通常為若干KB (x86架構中預設為4096位元組)。如果一臺機器有很多GB的記憶體,那麼頁面大小較小時,管理這些頁面的開銷就會很大,速度就會更慢。而大頁面使得頁面大小設定值最大可為256 MB (在ia64架構上)。然而使用大頁面意味著要將磁碟上一個扇區中幾MB的資料存放在記憶體中。如果資料不能全部存進記憶體,那麼從磁碟中載入大塊資料,只會更快地填滿記憶體,而這些內容隨後又會被移除出記憶體。此外,將對資料的修改重新整理到磁碟上也會更慢,因為磁碟寫入的“髒”資料必須達到幾MB,而非幾KB。

  注意:Windows系統將此特性稱為Large Pages而非hugepages。一些版本的Windows預設啟用該特性,而另一些版本則不會這樣做,因此應檢査確定該特性是否已被禁用。

  大頁面實際上是為了優化資料庫系統的效能而開發的,所以有經驗的資料庫系統管理員,可能會對本節內容感到驚訝。然而,MongoDB對磁碟所進行的順序訪問比一般的關係型資料庫要少得多。

3.4 選擇一種磁碟排程演算法

  磁碟控制器從作業系統接收到請求後,會使用一種排程演算法來決定處理這些請求的順序。有時改變這一演算法可提高磁碟效能。但對其他硬體和工作負載而言,可能沒什麼效果。最好的決定方法是進行實地測試。Deadline (截止時間)排程演算法和CFQ (completely fair queueing,完全公平佇列)排程演算法都是不錯的選擇。

  有時noop (“no-op”的縮寫,這是最簡單的排程演算法)排程演算法是最好的選擇。比如說處於虛擬化環境中使用noop排程演算法,該排程演算法可基本上以最快的速度把操作傳遞給下層的磁碟控制器,然後讓真正的磁碟控制器來處理所需的重新排序問題。

  類似地,在固態磁碟上,noop排程演算法通常是最好的選擇。固態磁碟並不存在機械磁碟中的磁頭位置問題。

  最後,如使用RAID控制器進行快取,則應使用noop排程演算法。快取的表現與固態磁碟類似,可高效地將寫入操作分配到不同的磁碟上去。

  可在啟動配置中使用--elevator選項來更改排程演算法。

  提示:該選項之所以被稱為elevator (電梯),是因為排程演算法的功能就像一部電梯,從不同的樓層(程序/時間)接收乘客(磁碟IO請求),再以一種可能的最佳方案,將之送至目的地。

  很多時候,所有的排程演算法都能很好地運作,可能感覺不到太大的區別。

3.5 不要記錄訪問時間

  系統預設記錄檔案最後被訪問的時間。由於MongoDB訪問資料檔案十分頻繁,如果禁止記錄這一時間,則會得到效能上的提升。在Linux系統中,可在/etc/fstab裡將atime更改為noatime,以禁止記錄訪問時間。

/dev/sda7    /data    ext4    rw,noatime    1    2

  要使該設定更改生效,需先重新掛載裝置。

  atime在舊的核心中(比如ext3)問題更大些,因為新的核心中使用relatime作為預設值,使得更新不會那麼頻繁。此外應注意,將此值設為noatime可影響其他程式使用分割槽,例如mutt或備份工具。

  類似地,在Windows系統下應設定disablelastaccess選項來實現相同功能。執行以下命令完成最後訪問時間記錄的禁止:

C:\> fsutil behavior set disablelastaccess 1

  需重啟使設定更改生效。該設定可能影響遠端儲存(Remote Storage)服務。不過由於該服務會自動移動資料到其他磁碟,所以本來也無需使用此服務。

3.6 修改限制

  MongoDB可能會受到兩個限制的影響:

    • 程序可建立執行緒的數量;
    • 程序能夠開啟檔案描述符(file descriptor)的數量。

  二者通常應被設定為無限制。

  客戶端與MongoDB伺服器建立連線時,伺服器就會建立一個執行緒來處理這個連線上發生的所有活動。因此,如果與資料庫建立了3000個連線,資料庫就會執行3000個執行緒(再加上幾個用於處理與客戶端無關任務的執行緒)。客戶端可與MongoDB建立十幾個甚至幾千個連線,具體數量取決於應用伺服器的配置。

  如果客戶端可動態地建立更多的子程序以應對增加的流量(大多應用伺服器都會這麼做),應確保這些子程序數量保持在MongoDB的限制以內。例如,如果有20個應用伺服器,其中每一個都被允許建立100個子程序,而每個子程序又可建立10個執行緒連線到MongoDB,那麼最多就可能會有20 x 100 x 10=20 000個連線。MongoDB面對這成千上萬的執行緒可能不會很高興,另外如果程序中的可用執行緒數被耗盡,則應拒絕新的連線。

  另一個需要修改的限制是,MongoDB能夠開啟的檔案描述符數量。每個連入和連出的連線都要使用一個檔案描述符,如果客戶端連線的數量真有如上一段描述的那樣,則會開啟20 000個檔案描述符(恰好這也是MongoDB所允許的最大數量)。

  特別是mongos,它會與很多分片建立連線。當客戶端連線到mongOS併發起請求時,mongos向所有所需的分片建立連線,以完成請求。於是,如果叢集中有100個分片,而客戶端連線到mongos並嘗試查詢所有資料,mongos就必須向每個分片建立一個連線,共計100個連線。這會促使連線數的快速增長,可依照之前的例子想象一下。假設一個配置不當的應用伺服器向mongos程序建立了 100個連線。也就是說對所有分片建立的連線數是100個連入連線x 100個分片=10 000個!(此處假設每個連線上的查詢都沒有特定目標,這種設計很差勁,所以這個例子有些極端)。

  因此可做些調整:很多人特意使用maxConns選項配置mongos程序,使其只允許特定數量的連入連線。這種方法可確保客戶端的正常工作。

  檔案描述符數量的限制也應該得到增加,因為其預設值(通常是1024)過低。將檔案描述符數目的最大值設為無限制,如果覺得不保險的話,可將其設為20 000。每個系統更改這些限制的方法有所不同,但通常來講,應確保對軟限制和硬限制都進行了修改。硬限制是核心級的,只有管理員可進行更改,而軟限制則是使用者級的。

  如果連線數限制為1024, MMS會在主機列表中將主機名稱顯示為黃色以示警告 (如上述NUMA的例子一樣)。如果限制值過低,“Last Ping”選項卡中會出現類似圖23-12中所示的資訊。

  即使不使用分片,應用程式建立的連線數也很少,也應至少將軟硬限制均增至 4096。這樣,MongoDB就不再會為此發出警告,也給了我們一些喘息空間,有備無患。

4.網路配置

  本節將介紹哪些伺服器間應建立連線。通常情況下,出於網路安全(和靈敏度)考慮,會希望限制MongoDB伺服器的網路連線。注意,多伺服器MongoDB的部署應能夠處理網路被隔斷的情況,但並不推薦將其作為一般情況下的部署方式。

  在獨立伺服器上,客戶端須能夠與mongod建立連線。

  副本整合員必須能夠連線到其他各成員。客戶端必須能夠連線到所有可見的非仲裁器成員。根據網路配置的不同,成員可能會嘗試與自身建立連線,所以應允許mongods建立到自身的連線。

  分片要稍微複雜一些。它由以下四種元件組成:

    • mongos伺服器;
    • 分片;
    • 配置伺服器;
    • 客戶端。

  連線要求可概括為以下三點:

    • 客戶端必須能夠連線到一個mongos伺服器;
    • mongos伺服器必須能夠連線到眾分片和配置伺服器;
    • 分片必須能夠連線到其他分片和配置伺服器。

  表23-1中顯示了完整的連線需求。

表23-1 分片連線

連線

從伺服器發出

與伺服器建立

mongos

分片

配置伺服器

客戶端

mongos

不需要

不需要

不需要

需要

分片

需要

需要

不需要

不推薦

配置伺服器

需要

需要

不需要

不推薦

客戶端

不需要

不需要

不需要

與MongoDB無關

  表格中有三種可能的值。

    • “需要”表示二者間應建立連線,以保證分片正常執行。若由於網路問題導致連線中斷的話,MongoDB會嘗試進行平穩退化,以儘可能地解決問題,但不應故意做如此配置。
    • “不需要”表示二者不會在指定方向進行通訊,也就無需建立連線。
    • “不推薦”表示二者間不會進行通訊,但使用者的錯誤操作則會促使相互通訊的完成。 例如,推薦做法是限制客戶端只與mongos建立連線,而不與分片建立連線,這樣客戶端就不會在無意中直接向分片請求內容。類似地,客戶端應無法直接訪問配置伺服器,以防意外更改配置資料。

  注意,mongos程序和分片會主動與配置伺服器進行通訊,但配置伺服器不會與任何其他節點,甚或是另一個配置伺服器建立連線。

  分片在進行遷移時必須進行通訊,因為可直接連線到其他分片以傳輸資料。 如前所述,組成分片的副本整合員應能夠與其自身建立連線。

5.系統管理

  本節我們將介紹一些在部署伺服器前應注意的常見問題。

5.1 時鐘同步

  一般來講,各系統間的時鐘誤差不超過一秒是最為安全的。副本集應能夠處理幾乎所有的時鐘偏移(clock skew)。分片則能夠處理一部分時鐘偏移(如偏移超過幾分鐘,日誌中會出現警告資訊),但最好將偏移降至最小。使時鐘保持同步,也使得査看日誌內容變得更加方便。

  為保持時鐘同步,在Windows系統中可使用w32tm工具,而在Linux系統中則可使用ntp後臺程序。

5.2 OOM Killer

  在極偶然的情況下,MongoDB會因分配過多記憶體而被OOM Killer (Out of Memory killer,記憶體溢位殺手)盯上。尤其是在建立索引時發生,這也是MongoDB的常駐記憶體會給系統造成壓力的少有情況之一。

  如果MongoDB程序突然被終止,而日誌中又沒有出現錯誤或退出資訊,則應檢査/var/log/messages (或核心記錄這些內容的其他位置),査看是否存在關於終止mongod程序的資訊。

  如果核心因為過度使用記憶體而終止了MongoDB程序,則應在核心日誌中看到如下內容:

kernel: Killed process 2771 (mongod)
kernel: init invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0

  如果開啟了日誌系統(journaling),此時重啟mongod程序即可。如沒有開啟日誌系統,則應恢復備份,或重新與副本進行同步。

  當系統沒有交換空間,並且可用記憶體開始減少時,OOM killer就會變得尤為敏感,因此為避免麻煩,不妨配置適當的交換空間。MongoDB應不會用到該交換空間,但這可讓OOM Killer放鬆下來。

  如果OOM killer終止了一個mongos程序,重啟它即可。

5.3 關閉定期任務

  檢查是否存在計劃任務或後臺程序,它們可能定期被啟用並消耗系統資源,比如軟體包管理器的自動更新。這些程式被啟用後會消耗大量的記憶體和CPU資源,然後又消失不見。我們不會希望在生產伺服器上見到這些東西。

作者:小家電維修

相見有時,後會無期。