讀古今文學網 > MongoDB實戰 > 10.4 性能調優 >

10.4 性能調優

最後這一節裡,我會提幾個與診斷和解決性能問題相關的內容。

大多數MongoDB的性能問題都能追溯到一個源頭:硬盤。本質上來看,對磁盤施加的壓力越大,MongoDB就運行得越慢,因此大多數性能優化的目標就是減少對磁盤的依賴。有多種方式能達到這個目標,但在我們深入瞭解它們之前,先瞭解如何弄清磁盤性能還是很有必要的。在Unix類系統上,iostat就是一款理想的工具。以下示例中,我通過-x選項顯示擴展統計信息,2說明以2 s為間隔進行顯示:1

1. 注意,該示例是針對Linux的;在Mac OS X上,對應命令是iostat -w 2

$ iostat -x 2
Device: rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdb 0.00 3101.12 10.09 32.83 101.39 1.34 29.36

Device: rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdb 0.00 2933.93 9.87 23.72 125.23 1.47 34.13
  

想要詳細瞭解每一個字段的含義,請查看系統的man頁面。要快速診斷問題,你會對最後三列最感興趣。await以毫秒為單位,標明了處理I/O請求的平均時間,該平均值包含了I/O隊列裡的時間和實際處理I/O請求的時間。svctime標明了處理請求所花費的平均時間。%util則是CPU用來處理磁盤I/O請求所耗時間的百分比。

之前的iostat片段顯示了一個中等程度的磁盤佔用情況。I/O的平均等待時間大約是100 ms(提示:這已經不少了!),平均處理時間大約是1 ms,利用率大約是30%。如果查看該機上的MongoDB日誌,你能看到很多慢操作(查詢、插入或其他操作)。事實上,正是那些慢操作最早讓你對潛在的問題有所警覺。iostat的輸出能幫助你確認問題。請注意,MongoDB系統的磁盤利用率達到100%是很常見的;雖然僅由MongoDB的問題造成磁盤利用率高的情況很少,但這些用戶還是覺得被MongoDB折騰得夠嗆。在後續的五個小節裡,我會給出一些優化數據庫操作、減輕磁盤負載的方法。

10.4.1 為提升性能檢查索引和查詢

發現性能問題時,應該首先檢查索引。這裡假設應用程序會發起查詢和更新,它們是使用索引的主要操作。2第7章大致羅列了識別並修復慢操作的步驟;其中涉及開啟查詢剖析器,隨後確保每個查詢與更新都能有效利用索引。總的來說,這意味著每個操作要掃瞄盡可能少的文檔。

2. 某些數據庫命令,比如count,同樣也會使用索引。

保證沒有冗余的索引也同樣重要,因為冗余索引會佔用磁盤空間、消耗更多內存,在每次寫入時還需要做更多工作。第7章中有消除此類冗余索引的方法。

然後呢?在審視了索引和查詢之後,你會發現效率低下的部分,把性能問題一起修正掉。日誌裡再也看不到慢查詢警告了,iostat的輸出也顯示利用率下降了。調整索引在修正性能問題方面的效果比你想的要好;在發現性能問題時,索引應該是你首先檢查的地方。

10.4.2 添加內存

修改索引並非總是有效,也許你已經有了最優化的查詢,有了完美的索引,但是磁盤利用率還是居高不下。此時,你應該看看索引尺寸和工作集對物理內存的比例。在應用程序所用到的每個數據庫上運行stats命令:

> use app
> db.stats
{
    \"db\" : \"app\",
    \"collections\" : 5,
    \"objects\" : 3932487,
    \"avgObjSize\" : 543.012,
    \"dataSize\" : 2135390324,
    \"storageSize\" : 2419106304,
    \"numExtents\" : 38,
    \"indexes\" : 4,
    \"indexSize\" : 226608064,
    \"fileSize\" : 6373244928,
    \"nsSizeMB\" : 16,
    \"ok\" : 1
}
  

現在看一下數據和索引的大小,數據大小剛超過2 GB,索引大小大約是230 MB。假設工作集包含了系統中的全部數據,那麼機器上至少要有3 GB內存才能避免頻繁訪問磁盤。要是這台機器只有1.5 GB內存,那你就只能眼睜睜看著磁盤利用率居高不下了。

在查看數據庫統計信息時,還要注意dataSizestorageSize之間的區別。當storageSize超過dataSize兩倍以上時,就會因磁盤碎片而影響性能。這些碎片會迫使服務器使用更多的內存;遇到這種情況,在添加更多的內存之前,應該先嘗試壓緊數據文件。請查看本章之前與壓緊有關的內容,瞭解如何進行壓緊。

10.4.3 提升磁盤性能

添加內存的方法也有一些問題。首先,並非總能添加內存,例如,你正在使用EC2,虛擬機的最大可用內存上限就是68 GB。其次,添加內存並非總能解決I/O問題。舉例來說,如果應用程序是寫密集型的,後台的刷新或者向內存加載新數據分頁依然會壓垮你的磁盤。所以說,如果有了高效的索引和充足的內存,但還是覺得磁盤I/O緩慢,就該想想提升磁盤性能了。

提升磁盤性能有兩種方法。一種是購買更快的磁盤,買塊15 K RPM的硬盤或者SSD還算是筆划算的投入。除此之外,把磁盤配置成RAID陣列,也能夠提升讀寫的吞吐量,這種方式也可以作為前者的補充。3如果配置得當,RAID陣列也許可以解決I/O瓶頸。正如之前所說的那樣,在EBS捲上運行RAID 10能顯著提升讀取的吞吐量。

3. RAID還有另一個好處,即在恰當的RAID級別下,能夠獲得磁盤冗余。

10.4.4 水平擴展

在解決性能問題時,下一步就該用到水平擴展了。你有兩條路可選。如果應用程序是讀密集型的,單個節點可能無法應對所有查詢,即使內存中是優化後的索引和數據。這時可以讓讀操作分佈在各個副本上。官方的MongoDB驅動支持跨副本集成員進行操作,在逐步提升到分片集群前,這個策略值得一試。

當所有其他手段都無效之時,就得進行分片了。滿足以下條件時,你就應該轉向分片集群了:

  • 無法將工作集完整加載到任意一台機器的物理內存裡;

  • 對任意服務器而言,寫負載太密集了。

要是在配置了分片集群之後,還是存在性能問題,那你就該回過頭去,確保所有索引都經過了優化、數據都在內存裡,並且磁盤性能良好。要獲得最佳的硬件利用率,你可能需要添加更多分片。

10.4.5 尋求專業幫助

造成性能下降的原因多種多樣,並且經常很奇特。從糟糕的Schema設計到詭異的服務器缺陷都能影響性能。如果在嘗試了各種可能的辦法之後,仍然無法解決問題,你應該考慮讓對MongoDB更有經驗的人士來檢查你的系統。一本書的作用有限,但是經驗豐富的專業人士所發揮的作用就大不相同了。當你不知所措或者心存疑慮時,請尋求專業幫助。性能問題的解決方案有時完全是憑直覺的。