首頁  >  文章  >  運維  >  MongoDB的磁碟IO問題的解決方法

MongoDB的磁碟IO問題的解決方法

巴扎黑
巴扎黑原創
2017-09-04 14:05:441437瀏覽

有點標題黨的意思,不過下面三招確實比較實用,內容來自Conversocial公司的VP Colin Howe在London MongoDB用戶群的一個分享。

申請:以下幾點並非放四海皆準的法則,具體是否能夠使用,還需要根據自己的應用場景和資料特性來決定。

1.使用組合式的大文檔

我們知道MongoDB是一個文檔資料庫,其每一個記錄都是一個JSON格式的文檔。例如像下面的例子,每一天都會產生一個這樣的統計數據:

{ metric: "content_count", client: 5, value: 51, date: ISODate("2012-04-01 13:00 ") }

{ metric: "content_count", client: 5, value: 49, date: ISODate("2012-04-02 13:00") }

#而如果採用組合式大文檔的話,就可以這樣將一個月的資料全部存到一筆記錄裡:

{ metric: "content_count", client: 5, month: "2012-04", 1: 51, 2 : 49, ... }

透過上面兩種方式存儲,預先一共存儲大約7GB的數據(機器只有1.7GB的內存),測試讀取一年信息,這二者的讀性能差別很明顯:

第一種: 1.6秒

第二種: 0.3秒

那麼問題在哪裡呢?

實際上原因是組合式的儲存在讀取資料的時候,可以讀取更少的文件數量。而讀取文件如果無法完全在記憶體中的話,其代價主要是被花在磁碟seek上,第一種儲存方式在取得一年資料時,需要讀取的文件數更多,所以磁碟seek的數量也越多。所以更慢。

其實MongoDB的知名用戶foursquare就大量採用這種方式來提升讀取效能。見此

2.採用特殊的索引結構

我們知道,MongoDB和傳統資料庫一樣,都是採用B樹作為索引的資料結構。對於樹狀的索引來說,保存熱資料所使用的索引在儲存上越集中,索引浪費掉的記憶體就越小。所以我們比較下面兩種索引結構:

db.metrics.ensureIndex({ metric: 1, client: 1, date: 1})

db. metrics.ensureIndex({ date: 1, metric: 1, client: 1 })

採用這兩種不同的結構,在插入表現上的差異也很明顯。

當採用第一種結構時,資料量在2千萬以下時,能夠基本保持10k/s 的插入速度,而當資料量再增大,其插入速度就會慢慢降低到2.5k/s,當資料量再增加時,其效能可能會更低。

而採用第二種結構時,插入速度能夠基本穩定在10k/s。

原因是第二種結構將date欄位放在了索引的第一位,這樣在建立索引時,新資料更新索引時,不是在中間去更新的,只是在索引的尾巴處進行修改。那些插入時間過早的索引在後續的插入操作中幾乎不需要進行修改。而第一種情況下,由於date欄位不在最前面,所以其索引更新經常是發生在樹狀結構的中間,導致索引結構會經常進行大規模的變更。

3.預留空間

與第1點相同,這一點同樣是考慮到傳統機械硬碟的主要操作時間是花在磁碟seek操作上。

例如還是拿第1點中的例子來說,我們在插入資料的時候,預先將這一年的資料所需的空間都一次插入。這能保證我們這一年12個月的資料是在一筆記錄中,是順序儲存在磁碟上的,那麼在讀取的時候,我們可能只需要一次對磁碟的順序讀取操作就能夠讀到一年的數據,比起前面的12次讀取來說,磁碟seek也只有一次。

db.metrics.insert([

    { metric: 'content_count', client: 3, date: '2012-01', 0: 0, 1: 0, 2: 0, ... }

    { .................................., date: '2012 -02', ... })

    { .................................. , date: '2012-03', ... })

    { ............................. ....., date: '2012-04', ... })

    { ........................ .........., date: '2012-05', ... })

    { ................... ..............., date: '2012-06', ... })

    { .............. ...................., date: '2012-07', ... })

    { ......... ................................, date: '2012-08', ... })

    { .... .............................., date: '2012-09', ... })

{ .................................., date: '2012-10', ... })

    { .................................., date: '2012-11', . .. })

    { .................................., date: '2012 -12', ... })

])

結果:

如果不採用預留空間的方式,讀取一年的記錄需要62ms

如果採用預留空間的方式,讀取一年的記錄只需要6.6ms

以上是MongoDB的磁碟IO問題的解決方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn