最後這一節裡,我會提幾個與診斷和解決性能問題相關的內容。
大多數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內存,那你就只能眼睜睜看著磁盤利用率居高不下了。
在查看數據庫統計信息時,還要注意dataSize
和storageSize
之間的區別。當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更有經驗的人士來檢查你的系統。一本書的作用有限,但是經驗豐富的專業人士所發揮的作用就大不相同了。當你不知所措或者心存疑慮時,請尋求專業幫助。性能問題的解決方案有時完全是憑直覺的。