1. 程式人生 > 其它 >Mongo伺服器管理之監控MongoDB

Mongo伺服器管理之監控MongoDB

  在部署前設定某種監控系統很是重要。監控系統應該能夠跟蹤伺服器正在執行的操作,也能夠在遇到問題時及時發出警報。以下介紹:

  • 如何跟蹤監測MongoDB的記憶體使用狀況;
  • 如何跟蹤監測應用的效能指標;
  • 如何診斷複製中的問題。

  本章以MMS(MongoMonitoringService,Mongo監控服務)為例,演示監控時應注意的內容。請於https://mms.10gen.com查詢MMS的安裝說明。如不想使用MMS,也可使用其他監控系統。監控系統可在故障發生前檢測到潛在問題,並有助於我們對於問題的診斷。

1.監控記憶體使用狀況

  訪問記憶體中的資料很快,而訪問磁碟中的資料則較慢。不幸的是,二者相比,記憶體較為昂貴,而MongoDB通常也會優先使用記憶體。本節將講述有關MongoDB與記憶體和磁碟進行互動的監控方式,以及監控中應關注的內容。

1.1 有關電腦記憶體的介紹

  電腦中一般會有容量小且訪問速度快的記憶體,以及容量大但訪問速度慢的磁碟。當請求一頁儲存於磁碟上但尚未存於記憶體中的資料時,系統就會產生一個缺頁中斷,而後將此頁資料從磁碟複製到記憶體。此後就可以極快地訪問記憶體中的頁面。如程式不再使用此頁面內容,而記憶體又被其他頁所佔滿,舊的頁面就會被清除出記憶體而只存在於磁碟上。

  將一頁資料從磁碟複製到記憶體,比從記憶體中讀取一頁資料耗時更長。因此,MongoDB從磁碟複製資料的操作越少越好。如果MongoDB能夠在記憶體中進行幾乎所有操作,則訪問資料的速度就能快很多。所以,MongoDB的記憶體使用情況,是要跟蹤監測的最重要指標之一。

1.2 跟蹤監測記憶體使用狀況

  MongoDB所使用的記憶體有幾種不同的型別。第一種是常駐記憶體(residentmemory):MongoDB在實體記憶體中明確擁有的記憶體部分。例如,在查詢文件時,該頁面即被載入記憶體中,併成為常駐記憶體的一部分。

  MongoDB賦予頁面一個地址,此地址並非實體記憶體中頁面的真實地址,而是一個虛擬地址。MongoDB可將此地址傳給核心,繼而由核心將其翻譯成真正的實體地址。這樣,即使核心需將此頁面從記憶體中清除出去,MongoDB依然可通過虛擬地址來訪問此頁面。MongoDB向核心請求記憶體,核心會在它的頁快取(pagecache)中進行查詢。但請注意,該頁並不在此處。査找失敗會產生缺頁中斷,繼而將此頁複製至記憶體,最後返回到MongoDB。這些MongoDB賦予了地址的資料頁面,即構成了對映記憶體(mappedmemory),其中包含了MongoDB訪問過的所有資料。通常情況下,對映記憶體的大小約等於整個資料集的大小。

  MongoDB為對映記憶體中的每個頁面,都額外維護了一個虛擬地址,以供日記(journaling)使用。這並不意味著記憶體中有著兩份同樣的資料,有的只是兩個地址而已。所以,MongoDB所使用虛擬記憶體的總量,約是對映記憶體的兩倍大小,或者說是整個資料集的兩倍大小。如關閉了日記機制,則對映記憶體的大小約等幹虛擬記憶體的大小。

  注意:虛擬記憶體和對映記憶體均不是‘‘真正的”記憶體分配:二者與實際佔用的記憶體大小毫無關係,它們只是MongoDB維護的對映罷了。理論上,MongoDB可對映1PB(petabyte, 1PB=1000TB=1 000 000GB)的記憶體,但實際只使用了幾GB的實體記憶體。所以不用擔心對映記憶體或虛擬記憶體的大小超過實體記憶體的容量。

  圖21-1是MMS中記憶體資訊的影象,描述了MongoDB所使用的常駐記憶體、虛擬記憶體和對映記憶體的大小。在一臺專門用於執行MongoDB的機器上,假設工作集(workingset)的大小不小於記憶體容量,則常駐記憶體的大小應稍小於總的記憶體大小。只有常駐記憶體的大小才確切地等於其在實體記憶體中所佔用的空間,但這一資料並不能說明MongoDB所使用的記憶體大小。

  資料如果能全部存放在記憶體中的話,則常駐記憶體應與資料差不多大小。當說到資料 “在記憶體中”時,通常指的是在實體記憶體中。

1.3 跟蹤監測缺頁中斷

  如圖21-1所示,記憶體的使用狀況通常比較穩定,但隨著資料集的增長,虛擬記憶體和對映記憶體也得到了增長。常駐記憶體會增長到可用實體記憶體的大小,而後保持不變。

  除了以每種記憶體各佔用多少空間為依據,還可通過其他資料得知MongoDB的記憶體使用方式。其中很實用的一個指標是發生缺頁中斷的數量,該數量表明瞭MongoDB所尋找的資料不在實體記憶體中這一事件的發生頻率。圖21-2和圖21-3為一段時間內發生缺頁中斷的次數。圖21-3中缺頁中斷的發生次數較少,但這一資料本身並不能說明很多問題。如果圖21-2中的磁碟能夠處理這麼多的缺頁中斷,而應用程式可以處理這些磁碟操作造成的延遲,那麼有這麼多缺頁中斷也沒什麼問題。另一方面,如果應用程式無法處理從磁碟中讀取資料造成的延遲,則只能將所有資料存放到記憶體中,或者使用固態硬碟(solidstatedrive,SSD)。

  無論應用能否處理這些延遲,缺頁中斷都會在磁碟超負荷時成為大問題。磁碟能夠處理的讀取運算元目並非是線性的:一旦磁碟開始超負荷執行,每個操作都必須排隊等候更長時間,從而引發連鎖反應。磁碟的超負荷執行通常存在一個臨界點,超出臨界點後磁碟的效能會迅速下降。因此,應避免磁碟的最大負荷運轉。

  監測一段時間內缺頁中斷的數量。如應用在某一數量的缺頁中斷下表現良好,則表明其為系統可處理的缺頁中斷數量底線。如應用效能在缺頁中斷上升至某一數值時開始發生惡化,則表明該數值是應發出警告的臨界值。

  在serverStatus命令輸出的recordStats欄位中,可看到每個資料庫的缺頁中斷情況:

>  db.adminCommand({"serverStatus" : 1})["recordStats"]
{
        "accessesNotlnMemory": 200632,
        "test": {
               "accessesNotInMemory": 1, 
               "pageFaultExceptionsThrown": 0
         },
         "pageFaultExceptionsThrown": 6633,
         "admin": {
                  "accessesNotlnMemory": 1247, 
                  "pageFaultExceptionsThrown": 1
           },
          "bat": {
                 "accessesNotInMemory": 199373, 
                 "pageFaultExceptionsThrown": 6632
            },
           "config": {
                  "accessesNotlnMemory": 0, 
                  "pageFaultExceptionsThrown": 0
            },
            "local": {
                  "accessesNotlnMemory": 2,
                  "pageFaultExceptionsThrown" : 0
             }
},

  其中的accessesNotInMemory表示,MongoDB自啟動以來必須去磁碟上讀取資料的次數。

1.4 減少索引樹的脫靶次數

  訪問不在記憶體中的索引條目時效率尤其低下,因為這一操作通常會造成兩次缺頁中斷,分別發生在將索引條目和文件載入入記憶體之際。査詢索引造成缺頁中斷時,我們稱其為索引樹的脫靶(btreemiss)。MongoDB也會監測索引樹中靶(btreehits)的次數,即無需到磁碟上訪問索引。圖21-4中可看到這兩個數值。

  索引十分常用,通常處於記憶體中,但如果記憶體過小而索引又過多,或訪問模式不正常(例如進行大量的表掃描),都會造成索引樹脫靶次數的增長。通常情況下,脫靶次數應保持在很小的數值,因此,一旦發現該數值過高,則須著手找出問題的源頭。

1.5 IO延遲

  IO延遲指CPU閒置等待磁碟響應的時間。通常情況下,該延遲與缺頁中斷密切相關。一些IO延遲是正常的,因為MongoDB有時須對磁碟進行訪問,且無法完全避免對其他操作的妨礙。重要的是,需保證IO延遲不再持續增長或增至100%左右。如圖21-5所示,這表明磁碟正在超負荷運轉。

  MMS可通過安裝外掛munin來監測CPU資訊。如需檢視安裝說明,請訪問https://mms.10gen.com/help/install.html#hardware-monitoring-with-munin-node

1.6 跟蹤監測後臺重新整理平均時間

  需關注的另一磁碟引數是,MongoDB將髒頁(dirtypage)寫入磁碟所花費的時間,即後臺重新整理平均時間(backgroundflushaverage)。該資料相當於一個警鐘。一旦所需時間開始延長,就表示磁碟的速度跟不上需要處理的請求。

  MongoDB預設會以至少每分鐘一次的頻率,將所有快取中的資料重新整理到磁碟中。(在有很多髒頁的情況下,MongoDB可能會以更高的頻率進行重新整理,這取決於作業系統。)可在啟動mongod時,通過--syncdelay選項後的引數,以秒為單位,來配置這一時間間隔的值。同步的頻率越高,每次同步的資料則更小,但效率也會隨之降低。

  提示:人們常誤以為syncdelay選項會影響資料永續性。但實際二者毫無關係。要想確保資料永續性,應使用日誌系統(journaling)。Syncdelay只用於調節磁碟效能。

  通常情況下,我們希望後臺重新整理平均時間能夠低於一秒。在繁忙的機器上或慢速磁碟上,該時間會有所延長,並且隨著磁碟超負荷的執行,所需時間會變得越來越長。在某一時刻,磁碟超出負荷太多,以至於資料重新整理用時超過60秒,這意味著MongoDB會不斷嘗試進行重新整理(這又對磁碟造成了更大的負擔)。磁碟重新整理時間偶爾出現高峰是可以接受的。但不斷出現數十秒的長時間寫入則是我們不希望看到的。

  圖21-6為後臺重新整理平均時間的曲線變化圖。該系統的硬碟驅動器壓力很大,總是需要大於5秒的時間來寫入前一分鐘產生的資料。速度有些慢,尤其是經常會出現近20秒時長的高峰期,所以可能有必要將syncdelay的值調低一些,比如說40秒,然後看看每次重新整理較少的資料是否會有幫助。

  如果後臺重新整理平均時間長時間超出磁碟所能承受的值(可能只超了幾秒鐘),就應該開始考慮如何減輕磁碟的負載。

  MongoDB只需重新整理髒資料(即發生更改的資料),所以後臺重新整理平均時間通常反映出寫入負載的大小,即寫入操作和寫入資料的數量。因此,如果寫入負載很低,後臺重新整理平均時間可能無法表現出磁碟的壓力大小。除後臺重新整理平均時間外,還應同時監測IO延遲和缺頁中斷的情況。

2.計算工作集的大小

  通常情況下,記憶體中資料越多,MongoDB的執行速度就越快。因此,應用可遇到如下情況(執行速度從快到慢排列)。

    (1) 整個資料集均在記憶體中。雖然這種情況很不錯,但通常代價過大或不可行。此種情況可能需要應用的響應速度足夠快才能達成。

    (2) 工作集處於記憶體中。這是最常見的選擇。

工作集是應用所使用的資料和索引。這可能是其所有內容,但通常來講會存在一個能夠覆蓋90%請求的核心資料集(如使用者集合和最近一個月的活動)。如該工作集存在於實體記憶體中,MongoDB的執行速度通常會很快,因為它只有在遇到少數“不尋常”的請求時才需訪問磁碟。

    (3) 索引處於記憶體中。

    (4) 索引的工作集處於記憶體中。通常需要右平衡索引才能達成此種情況。

    (5)記憶體中沒有可用的資料子集。可能的話,應避免這種情況。這會使資料庫執行緩慢。

  我們必須通過了解工作集的內容及大小來判斷能否將其存入記憶體。計算工作集大小的最好方式是跟蹤分析一些常用的操作,從而找出應用的讀寫資料有多少。例如,假設應用每週會建立2GB的新資料,而其中800MB是經常被訪問的。使用者通常只會訪問近一個月的資料,更早的資料則通常不會被用到。這樣工作集大小可能是3.2GB(800MB/周X4周)左右,再根據經驗估計一下索引大小,加起來大概是5GB。

  可通過跟蹤監測一段時間內被訪問的資料來考慮這一問題,如圖21-7所示。如選擇儘快滿足90%的請求,則這一時間段內生成的資料和索引即為工作集,如圖21-8所示。可測查這一時間的長短,從而計算出資料集的增長情況。注意,此例使用了時間,即資料的新舊作為引數,但同時可能存在更適用於應用的訪問模式(時間是最常用的一種)。

      圖21-7: 資料新舊程度與被訪問次數的關係圖

圖21-8: 工作集即經常進行的請求所訪問的資料

  還可通過MongoDB的狀態來估計工作集的大小。MongoDB保留有一個記錄記憶體內容的圖表,可將“workingSet”: 1引數傳入serverStatus來得知這些內容。

> db.adminCommand({"serverStatus" : 1, "workingSet" : 1})
{
...
        "workingSet" : {
                 "note" : "thisIsAnEstimate", 
                 "pagesInMemory" : 18, 
                 "computationTimeMicros" : 3685, 
                 "overSeconds" : 2363
          },
...
}

  pagesInMemory指MongoDB認為當前記憶體中的頁面數目。實際上,MongoDB並不知道其確切數值,但結果應該很接近。在返回資訊中,如果記憶體中的頁面數目與記憶體大小相等,則該數值沒有什麼價值;但如果頁面數目小於記憶體大小,則該數值可能與工作集的大小有關。

  serverStatus的返回結果預設不包含workingSet欄位。

  一些工作集的例子

  假設工作集大小為40GB。90%的請求能夠命中工作集,其他10%則需訪問工作集以外的資料。如果有500GB的資料和50GB的記憶體,則工作集可全部放入記憶體中。一旦應用訪問了需經常訪問的資料(即預熱過程),則無需在訪問工作集時再次訪問磁碟。有10GB的空間提供給460GB不常訪問的資料。顯然,MongoDB幾乎總是要到磁碟上訪問工作集以外的資料。另一方面,假設工作集無法放入記憶體。比如只有35GB的記憶體。這種情況下工作集通常會佔據大部分的記憶體。工作集中的內容經常被訪問,因而更有可能留在記憶體中,但有時不常訪問的資料也會被載入記憶體,從而將工作集(或其他不常訪問的資料)擠出記憶體。於是,記憶體和磁碟會頻繁進行資料交換,此時無法再預測訪問工作集中資料的效能。

3.跟蹤監測效能狀況

  査詢的效能通常應重點監測並使其保持穩定。有幾種方式可用來監測MongoDB是否能承受當前的請求負荷。

  MongoDB佔用CPU時,大部分時間花在了處理器的讀寫上(IO延遲很高,其他指標可忽略)。然而,如果使用者或者系統佔用的CPU時間接近100% (或者100%乘以CPU的數量),最可能的原因是一個常用的査詢缺少合適的索引。另一種可能性是運行了太多的MapRedues或其他的伺服器端JavaScript指令碼。有必要跟蹤監測CPU,從而確保所有査詢的表現與預想中的相符,特別是在部署了一個新版本的應用之後。

  注意,圖21-9中顯示的是正常的,如果缺頁中斷的數量較低,IO延遲可能被其他CPU活動所拖累。只有在其他活動增長時,缺少合適的索引才可能是罪魁禍首。

  另一個相似的指標是佇列長度,即有多少請求正在等待MongoBD的處理。請求在等待鎖進行讀寫操作時,即被認為是處於佇列中。圖21-10為讀寫佇列隨時間變化的影象。不存在佇列為最佳(此時影象基本為空白),但無需針對這一指標發出警報。在一個繁忙的系統中,操作需耗時等待以獲取所需的鎖,這一點很常見。

  可通過佇列中的請求數量,判斷是否發生了阻塞。通常佇列的長度應該很低。一個很長且始終存在的隊列表示mongod無法承受其負載。應儘快減輕該伺服器的負荷。可將佇列長度和鎖比例(lockpercentage)兩個指標結合起來,鎖比例指MongoDB處於鎖定中的時間。一般來講,相較於發生鎖定,磁碟IO更傾向於限制寫入。但依然有必要對鎖定進行跟蹤監測,尤其是磁碟速度快,或連續寫入多的系統。重複一遍,鎖比例過高的最普遍原因之一就是缺少了合適的索引。隨著鎖比例的增加,操作取得鎖所需的平均等待時間越來越長。因此,過高的鎖比例會將所有東西拖慢,導致請求堆積,以及系統中更高的負荷和更高的鎖比例。圖21-11中顯示了極高的鎖比例,這種情況應儘快得到處理。

  隨著流量大小的變化,鎖比例常會發生起伏變化。但如果鎖比例長時間保持上升趨勢,則表明系統所受的壓力較大,應做一些調整。因此,應在鎖比例長時間保持過高的值後再觸發警報(這樣當流量突然增加時就不會觸發警報了)。

  另一方面,我們可能也希望在鎖比例突然升高時,比如說高於正常值25%時觸發警報。該數值可能表明系統無法承載突然升高的負荷,也許應該提高系統的效能和容量了。

  除全域性的鎖比例外,MongoDB也對每個資料庫的鎖比例進行跟蹤。因此,如果某資料庫有很多的連線,可單獨檢視其鎖比例。

  跟蹤監測空餘空間

  另一基本但卻很重要的監測指標為磁碟的使用情況,即監測磁碟的空餘空間。有時使用者直到磁碟空間被佔滿時才想起處理這一問題。通過監測磁碟使用情況,可預測當前磁碟的使用時間,併為磁碟空間不足提前做好準備。

  磁碟空間不足時,有以下幾個選項。

    • 如果在使用分片,那就增加一個分片。
    • 依次關閉副本集中的每個成員,複製資料到更大的磁碟上進行掛載。重啟該成員,然後對下一成員進行同樣的操作。
    • 把副本集中的成員替換成更大驅動器的成員:移除舊成員,新增新成員。使新成員追趕上副本集中的其餘成員。對集合中的每個成員重複此操作。
    • 如使用了directoryperdb選項,且資料庫增長速度非常快,可將資料庫移至其驅動器內。掛載驅動器為資料目錄。這樣就可不必移動其他資料內容了。

  無論採取哪種方法,請提前做好準備,從而使對應用產生的影響降至最低。請先做好備份,依次修改副本集中的每個成員,並將資料從一處複製至另一處。

4.監控副本集

  對副本集中的落後(lag)和oplog(operationlog)長度進行跟蹤監測十分重要。

  當備份節點無法與主節點保持一致時,就產生了落後。主節點最後一次操作的時間和備份節點最後一次操作的時間差值,即落後的值。例如,一個備份節點剛剛完成了一次操作,其時間戳為3:26:00p.m.,主節點剛剛完成了一次操作,其時間戳為3:29:45p.m.,此時落後的值即為3分45秒。落後的值越接近0越好,且通常為毫秒級別。如果一個備份節點能夠與主節點保持同步,副本集落後的值應如圖21-12所示,基本保持為0。

  如果備份節點的複製速度趕不上主節點的寫入速度,就會開始出現非0的落後值。最極端的情況是副本集發生了阻塞:由於某種原因,副本集無法再接受任何操作。這種情況下,每經過一秒,落後的值就會增加一秒,在影象中呈現一個陡坡的樣子,如圖21-13所示。這可能是由於網路問題引起的,也可能是由於缺少 _id 索引,副本集要求每個集合都擁有這一索引才能正常工作。

  如果集合缺少了 _id 索引,將伺服器脫離副本集作為一個獨立伺服器啟動,然後建立_id索引。確保建立的 _id 索引是唯一索引(unique index)。索引建立完成後,除非刪除整個集合,否則 _id 索引不能發生刪除或更改。

  如系統超負荷執行,備份節點可能會逐漸被主節點落下。但圖中通常不會顯示出特徵明顯的“每秒增加一秒”的陡坡,因為備份節點還是進行了一些複製的。然而,備份節點到底是因為無法與高峰流量保持一致而被落下的,還是逐漸被主節點落下的,這一點十分重要。

  主節點不會為了“幫助”備份節點追趕上來而限制寫入,所以在超負荷執行的系統上備份節點追趕不上的情況時有發生(尤其是MongoDB中寫入的優先順序比讀取要髙,這意味著副本集的效能很大程度上取決於主節點)。可在寫入時使用“w”引數來強制限制主節點的寫入。也可通過將請求路由至其他成員,從而降低備份節點的負載。

  而在一個負載極低的系統上,可在副本集落後值的影象中看到另一種有趣的圖案,即突然出現的高峰值,如圖21-14所示。這些峰值表示的並不是真正的落後,而是由抽樣的變化產生的。mongod每隔幾分鐘處理一個寫入操作。落後的值是主節點和備份節點的時間戳差值,而對備份節點時間戳的測量恰好發生在主節點的寫人操作之前,這使得備份節點看起來好像落後了幾分鐘一樣。如果增加寫入頻率,這些峰值就會消失。

  另一需要跟蹤監測的重要指標是每個成員的oplog長度。每個可能成為主節點的成員都應擁有一份長度超過一天的oplog。如一個成員可能成為另一個成員的同步源(syncsource),則應擁有一份長度足夠進行初始化同步(initialsync)的oplog。圖21-15為標準的oplog長度影象。該長度極佳,達1111小時,即超過一個月的資料!通常,在保證磁碟空間充足的前提下,oplog應儘可能地長。oplog幾乎不佔用記憶體。而且長oplog的缺乏,可能會帶來痛苦的回憶。

  圖21-16為較短的oplog和變化的流量引起的稍顯不同尋常的變化。執行仍舊正常, 但該機器上的oplog可能太短了(6到11小時的維護時段)。管理員有機會的話應將該oplog的長度加以延長。

作者:小家電維修

相見有時,後會無期。