天天看點

Mongo應用管理之了解Mongo的動态

  啟動并運作應用後,要如何知道它正在做些什麼呢?本章将介紹如何了解MongoDB 正在進行何種査詢,有多少資料正在寫入,以及如何探查MongoDB具體正在做些什麼。

如何找到并終止那些拖慢速度的操作;

擷取并分析有關集合和資料庫的統計資料;

用指令行工具來了解MongoDB正在做些什麼。

  要想找到是哪些操作拖慢了速度,看看正在進行的操作不失為一種簡單的方法。速度慢的操作耗時更長,更有可能被發現。雖然不能保證一定會有結果,但這是個不錯的開始。

  査看正在進行的操作,可使用db.currentOp()函數:

  該函數會列出資料庫正在進行的所有操作,輸出的資訊中有些重要的字段。

opid

  這是操作的唯一辨別符(identifier),可通過它來終止一個操作。

active

  表示該操作是否正在運作。如這一字段的值是false,意味着此操作已交出或正在等待其它操作交出鎖。

secs_running

  表示該操作已經執行的時間。可通過它來判斷是哪些查詢耗時過長,或者占用了過多的資料庫資源。

desc

  該值可與日志(log)資訊聯系起來。日志中與此連接配接相關的每一條記錄都會以[conn3]為字首,是以可以此來篩選相關的日志資訊。

locks

  描述該操作使用的鎖的類型。其中“^”表示全局鎖。

waitingForLock

  表示該操作是否因正在等待其他操作交出鎖而處于阻塞狀态。

numYields

  表示該操作交出鎖(yield),而使其他操作得以運作的次數。通常,進行文檔搜尋的操作(査詢、更新和删除)可交出鎖。隻有在其他操作列隊等待該操作所持的鎖時,它才會交出自己的鎖。簡單地講,如果沒有其他操作處于waitingForLock狀态,則該操作不會交出鎖。

lockstats.timeAcquiringMicros

  表示該操作需要多長時間才能取得所需的鎖。

  在執行currentOp({“ns”: “prod.users”})時,可添加過濾條件,進而隻顯示符合條件的結果。例如,隻顯示在某一命名空間中進行的操作,或隻顯示已運作了一定時間的操作。把査詢條件作為參數傳入函數來進行過濾:

  對于currentOp中的任何字段都可以進行查詢,使用普通的查詢語句即可。

  db.currentOp()最常見的作用就是用來尋找速度較慢的操作。可采用上一節中提到的過濾方法,來查找哪些查詢消耗的時間超過了一定的值。也許能通過該方法找出哪裡缺少了索引,或是進行了不恰當的條件過濾。

  有時會發現正在運作一些不明査詢,這通常是由于一個應用伺服器在運作一個舊的或有漏洞的軟體版本所導緻的。"client"字段可用來幫助追蹤找出這些不明操作的來源。

  隻要找到了想要終止的操作,就可将該操作的opid作為參數,通過執行 db.killOp()來終止該操作的執行:

  并非所有操作都能被終止。一般來講,隻有交出了鎖的程序才能被終止,是以更新 (update)、查找(find)、删除(remove)操作都可被終止。正在占用鎖,或正在等待其他操作交出鎖的操作則通常無法被終止。

  如果向一個操作發出了“kill”信号,那麼它在db.currentOp的輸出中就會有一個killed字段。然而,隻有從目前操作清單消失後,它才會真正的得到終止。

  在査找哪些操作耗時過長時,可能會發現一些長時間運作的内部操作。根據設定, MongoDB可能會長時間地執行若幹請求。最常見的是用于複制(replication)的線程(它會持續向同步源請求更多的操作)和分片中用于回寫(writeback)的監聽器(listener)。所有local.oplog.rs中的長時間運作請求,以及所有回寫監聽指令,都可以被忽略掉。

  如以上操作被終止,MongoDB則會重新開機它們。不過,通常我們不應該這麼做。終止用于複制的線程會短暫地中止複制操作,而終止掉回寫監聽器則可能會造成mongos遺漏正常的寫入錯誤。

  這是一個不常見的,隻有在MongoDB中才可能會遇到的問題,尤其是在進行靜态加載(bulk-loading)資料至集合的時候。假設現在我們建立了一個任務(job),用于在MongoDB中進行上千條更新操作,而MongoDB正逐漸趨于停止。我們迅速停止了這一任務,終止了正在進行的所有更新操作。然而,我們會發現新的更新操作不斷出現,哪怕任務已經不再運作!

  如果使用非應答式寫入(unacknowledge write)加載資料,應用觸發寫入操作的速度可能要比MongoDB處理的速度更快。如MongoDB有所準備,這些寫入會堆積在作業系統的套接字緩存(socket buffer)中。終止掉MongoDB正在進行的寫入操作後,MongoDB則開始處理緩存區中的寫入操作。即使停止用戶端發送,MongoDB也會處理這些緩存中的寫入請求,因為它們已經被MongoDB所接收了,隻不過還沒有進行處理而已。

  阻止這些幽靈寫入的最好方式是使用應答式寫入,即每次寫入操作都會等待上一次寫入操作完成後才會進行下去,而非在上一次寫入進入資料庫伺服器的緩存區就開始下一次寫入。

  可利用系統分析器(system profiler)來査找耗時過長的操作。系統分析器可記錄特殊集合system.profile中的操作,并提供大量有關耗時過長的操作資訊,但相應的,mongod的整體性能也會有所下降。是以,我們可能隻需定期打開分析器來擷取資訊即可。如系統已經負載過重,則建議使用本章介紹的另一方法來解決問題。

  預設情況下,系統分析器處于關閉狀态,不會進行任何記錄。可在shell中運作db.setProfilingLevel()開啟分析器:

  以上指令将分析器的級别設定為2級,意味着“分析器會記錄所有内容”。資料庫收到的所有讀寫請求都将被記錄在目前資料庫的system.profile集合中。每一個資料庫都啟用了分析器,這也将帶來大量的性能損失,因為每一次寫操作都會增加額外的寫入時間,而每一次讀操作都要等待寫鎖(因為它必須在system.profile集合中寫入記錄)。然而,它也會提供給我們系統進行操作的詳盡清單:

  在"client"(用戶端)字段中可看到各操作是由哪個使用者發送至資料庫的。如果啟用了身份驗證系統,也能夠看到各操作是由哪些使用者運作的。

  一般情況下,我們隻想關注那些耗時過長的操作,而非資料庫中正在進行的所有操作。為此,可将分析器的分析級别設為1,即隻顯示長耗時操作。級别為1的分析器會預設記錄耗時大于100 m的操作。也可以自定義“耗時過長”的标準,把這個值作db.setProfiUingLevel()函數的第二個參數。以下指令會記錄所有耗時超過500 ms的操作:

  将分析級别設為0可關閉分析器。

  通常情況下,不要将slowms的值設得過小。即使分析器處于關閉狀态,slowms也會對mongod有所影響,因為它決定了哪些操作将作為耗時過長操作被記錄到日志中。是以,如果将slowms設為2 ms,那麼哪怕分析器是關閉着的,每個耗時超過2 ms的操作也都會出現在日志裡。是以,如果出于某些需求降低了 slowms的值,那麼應在關閉分析器前将它重新調高。

  可通過db.getProfilingLevel()來査看目前的分析級别。分析級别的設定值會在重新開機資料庫後被清除。

  也可在指令行中使用--profile level和--slowms time選項來配置分析器的級別。但更改分析級別通常隻是在調試時作為一種臨時措施,而不應該将其長期地加入配置中。

  如開啟了分析器而system.profile集合并不存在,MongoDB會為其建立一個大小為若幹MB的固定集合(capped collection)。如希望分析器運作更長時間,可能需要更大的空間來記錄更多的操作。此時可關閉分析器,删除并重建立立一個新的名為system.profile的固定集合,并令其容量符合需求。然後在資料庫上重新啟用分析器。

  如能得知文檔、索引、集合、資料庫各占用了多少空間,就可以友善地預留出合适的磁盤和記憶體空間。其它文章會詳細講解計算方式,這裡不做過多介紹

  要査詢文檔占用的空間大小,最簡單的方法是在shell中對文檔使Object. bsonsize()函數。此函數将傳回該文檔存儲在MongoDB中時占用的空間大小。

  例如,我們可以看到,将_id存儲為ObjectId類型,比存儲為字元串類型效率更高。

  也可以直接對集合中的文檔進行査詢:

  這一函數會精确地告知文檔在磁盤上占用的位元組數目。然而這其中并未包括自動生成的空間間隔(padding)和索引,二者也時常是影響集合大小的重要因素。

  stats函數可用來顯示一個集合的資訊:

  stats函數的傳回結果中首先是命名空間(即brains.boards),接下來是集合中文檔的數目。再接下來的幾個字段與集合的大小有關。size的值相當于對此集合中的所有元素執行Object.bsonsize(),再将這些結果相加得到的值,即集合中所有文檔占有的位元組數。将avgObjSize(平均對象大小)和count相乘,也能得到size的值。

  與之前提到的一樣,所有文檔占用的位元組總數并不等于集合大小,集合還占用空間存放其他重要内容,即文檔間的間隔和索引資訊。而storageSize不僅包含這些内容,還包含集合兩端預留的未經使用空間。集合末端總有些空餘空間,以便新文檔能夠快速添加進來。

  nindexes是集合中索引的數量。索引直到建立完成後才會被算在nindexes中,也隻有在出現在此清單後才可以被使用。由于目前的集合還很小,是以每個索引都隻有一個“桶”(bucket)大小(8 KB)。通常來講,索引比存儲的資料量大很多,含有很多空閑空間,以便在增加新入口(entry)時進行優化。使用右平衡索引。可将這一空閑空間減至最小。而随機分布的索引通常會有50%左右的空閑空間,升序索引(ascending-order index)則有10% 的空閑空間。

  随着集合的不斷增長,stats()傳回的巨大位元組數目可能會變得不易辨識。是以,可在使用stats時傳入比例因子(scale factor) : KB值為1024,MB則為1024X 1024,依次類推。例如,以下指令會以TB為機關顯示集合資訊:

  資料庫的stats函數與集合的類似:

  首先傳回的是資料庫名稱和其中包含的集合數目。object的值是資料庫中所有集合包含的文檔總數。

  輸出中包含了有關資料大小的資訊。fileSize應該總是最大的,即為資料庫檔案配置設定的總空間。該值應等于資料目錄中所有名為brains.*的檔案大小總和。

  第二大的字段通常是storageSize,即資料庫正在使用的總空間大小。該值與 fileSize不符,因為fileSize包含了預配置設定(preallocated)檔案。例如,如果資料目錄中已經存在brains.0、brains.1和brains.2檔案,則brains.2會被0填滿。brains.2寫入資料後,檔案brains.3會被預配置設定。每個資料庫内應一直存在一個填充為0的空檔案。該空檔案被寫入資料後,下一個檔案則會被預配置設定。是以,該空檔案(以及前面檔案中未被使用的部分)造成了fileSize和storageSize間的差異。

  dataSize是此資料庫中的資料所占用的空間大小。注意,該值并不包含空閑清單(freelist)中的空間,但包含了文檔間的間隔。是以該值與storageSize值的差異,應為被删除文檔的大小。

  與集合的stats()—樣,db.stats()可接收一個比例因子作為參數。

  如果對一個不存在的資料庫使用db.stats(),則nsSizeMB的值為0。這是.ns檔案的大小,它本質上相當于資料庫中的内容表。任何存在的資料庫均需一個.ns檔案。

  記住,在一個繁忙的系統上列出資料庫資訊會非常慢,而且會阻礙其他操作。是以應盡量避免此類操作。

  MongoDB自帶了幾個指令行工具,可通過每隔幾秒輸出目前狀态,幫助我們判斷資料庫正在做些什麼。

  mongotop類似于UNIX中的top工具,可概述哪個集合最為繁忙。可通過運作 mongotop-locks,進而得知每個資料庫的鎖狀态。

  mongostat提供有關伺服器的資訊。mongostat預設每秒輸出一次包含目前狀态的清單,可在指令行中傳入參數更改時間間隔。每個字段都會給出自上一次被輸出以來,所對應的活動發生次數。

insert / query / update / delete / getmore / command

  每種對應操作的發生次數。

flushes

  mongod将資料重新整理(flush)到磁盤的次數。

mapped

  moiigod所映射的記憶體數量,通常約等于資料目錄的大小。

vsize

  mongod正在使用的虛拟記憶體大小,通常為資料目錄的2倍大小(一次用于映射的檔案,一次用于日記系統)。

res

  mongod正在使用的記憶體大小,通常該值應盡量接近機器的所有記憶體大小。

locked db

  在上一個時間片中,鎖定時間最長的資料庫。該百分比是根據資料庫被鎖定的時間和全局鎖的鎖定時間來計算的,這意味着該值可能超過100%。

idx miss %

  輸出中最令人困惑的字段名。指有多少索引在通路中發生了缺頁中斷(page fault),即索引入口(或被搜尋的索引内容)不在記憶體中,使得mongod必須到磁盤中進行讀取。

qr|qw

  讀寫操作的隊列(queue)大小,即有多少讀寫操作被阻塞,等待進行處理。

ar|aw

  指活動用戶端的數量,即正在進行讀寫操作的用戶端。

netIn

  通過網絡傳輸進來的位元組數,由MongoDB進行統計(不必和作業系統的統計相等)

netOut

  通過網絡傳輸出的位元組數,由MongoDB進行統計。

conn

  此伺服器打開的連接配接數,包括輸入和輸出連接配接。

time

  指以上統計資訊所用時間。

  可在副本集或分片叢集上運作mongostat。如使用- -discover選項, mongostat會嘗試在初始連接配接的成員中尋找副本集或分片叢集中的所有成員,毎台伺服器也會每秒針對每個成員輸出一行資訊。對于較大叢集而言,該選項會使資料輸出過多過快而不易于管理,但于較小叢集而言卻很實用,也可使用一些工具将其輸出的資訊轉換為更可讀的形式。

  要想獲得資料庫中正在進行的操作快照,mongostat是很好的選擇,但如果要對資料庫進行長期的監控,類似MMS的工具可能更為适合(其它文章講解)。

作者:小家電維修

相見有時,後會無期。