1. 程式人生 > >MongoDB與MySQL的插入、查詢效能測試

MongoDB與MySQL的插入、查詢效能測試

1. 背景介紹

1.1  MongoDB的簡單介紹

在當今的資料庫市場上,MySQL無疑是佔有一席之地的。作為一個開源關係型資料庫,MySQL被大量應用在各大網站後臺中,承擔著資訊儲存的重要作用。2009年,甲骨文公司(Oracle)收購Sun公司,MySQL成為Oracle旗下產品。

而MongoDB是一種檔案導向的資料庫管理系統,屬於一種通稱為NoSQL的資料庫,是10gen公司旗下的開源產品,其內部資料儲存的方式與傳統的關係型資料有很大差別。

NoSQL的全稱是Not Only SQL,也可以理解非關係型的資料庫,是一種新型的革命式的資料庫設計方式,不過它不是為了取代傳統的關係型資料庫而被設計的,它們分別代表了不同的資料庫設計思路。

雖然MongoDB背後的公司沒有Oracle強大,但其目前也正在被應用在各行各業中。MongoDB是目前被應用最廣泛的NoSQL資料庫產品。

1.2  MongoDB的儲存特點

在傳統的關係型資料庫中,資料是以表單為媒介進行儲存的,每個表單均擁有縱向的列和橫向的行。以MySQL為例,如果使用者想以學生的學號為索引,存入其姓名與住址資訊時,資料庫中存放的資訊便如下圖所示:

ID

Name

Address

10001

Alice

A1

10002

Bob

A2

10003

Cara

A3

10004

David

A4

10005

Eve

A5

上圖表明資料庫中存入了5個表項,其記錄了學號為10001 – 10005的學生的姓名與住址資訊。

如果使用者欲把相應的資訊重新存入MongoDB資料庫,那麼資料庫中的資訊應該為如下所示:

"_id" : "10001",

"Name" : "Alice",

"Address" : "A1",

"_id" : "10002",

"Name" : "Bob",

"Address" : "A2",

"_id" : "10003",

"Name" : " Cara",

"Address" : "A3",

"_id" : "10004",

"Name" : " David",

"Address" : "A4",

"_id" : "10005",

"Name" : " Eve",

"Address" : "A5",

由此可見,相比較MySQL,MongoDB以一種直觀文件的方式來完成資料的儲存。它很像JavaScript中定義的JSON格式,不過資料在儲存的時候MongoDB資料庫為文件增加了序列化的操作,最終存進磁碟的其實是一種叫做BSON的格式,即Binary-JSON

對比兩個資料庫中資料儲存的差異,你可能沒有什麼特別的直觀感受。讓我們再看看MongoDB存放的另一組資料:

"_id" : "10001",

"score" : {

           "Maths" : 71,

           "English" : 62,

}

"_id" : "10002",

"score" : {

           "Maths" : 81,

           "Chemistry" : 74,

           "Sport" : {

                       "Basketball" : 67,

                       "Badminton" : 76,

                   },

}

上述資料表明了學號為10001與10002兩名學生的課程分數資訊。如果想把同樣的資料存入MySQL資料中的話,勢必要很費一番功夫。在關係型資料庫中,列的數目一般事先固定,各列之間可以由列名來識別。如果想存入以上資料,我們可以採取如下方法:

ID

Maths

English

Chemistry

Basketball

Badminton

10001

71

62

null

null

null

10002

81

null

74

67

76

或者如下這種:

ID

Course

Score

Course

Score

Course

Score

Course

Score

10001

Maths

71

English

62

null

null

null

null

10002

Maths

81

Chemistry

74

Basketball

67

Badminton

76

上述兩種儲存方式無論選哪一種,都不能很直觀地呈現兩名學生的各科成績與各學科之間隸屬關係,在儲存空間上的利用也不盡如意,並且可擴充套件性也不夠好。當然,為了解決這些問題,我們還可以使用多張表單來儲存學生的成績,但這樣也會使資料庫中的內容更加複雜。

1.2  MongoDB的應用場景

在另一方面,對開發者來說,如果是因為業務需求或者是專案初始階段,而導致資料的具體格式無法明確定義的話,MongoDB的這一鮮明特性就脫穎而出了。相比傳統的關係型資料庫,它非常容易被擴充套件,這也為寫程式碼帶來了極大的方便。

不過MongoDB對資料之間事務關係支援比較弱,如果業務這一方面要求比較高的話,MongoDB還是並不適合此型別的應用。

另外,MongoDB出現的時機比較晚,還具備一些非常鮮明的特性。比如:

1.       它裡面自帶了一個名叫GirdFS的分散式檔案系統,這就為MongoDB的部署提供了很大便利。而像MySQL這種比較早的資料庫,雖然市面上有很多不同的分表部署的方案,但這種終究不如MongoDB直接官方支援來得便捷實在。

2.       另外,MongoDB內部還自建了map-reduce運算框架的支援,雖然這種支援從功能上看還算是比較簡單的,相當於MySQL裡GroupBy功能的擴充套件版,不過也為資料的統計帶來了方便。

3.       MongoDB在啟動後會將資料庫中的資料以檔案對映的方式載入到記憶體中。如果記憶體資源相當豐富的話,這將極大地提高資料庫的查詢速度,畢竟記憶體的I/O效率比磁碟高多了。

但是,作為一個新鮮的事務,MongoDB也存在著很多不足。它在為開發人員提供了便利的情況下,卻在運維上面臨著不少難題,比如:

1.       比起MySQL,MongoDB沒有成熟的運維經驗,需要不斷地探索。

2.       MongoDB中的資料存放具有相當的隨意性,不具有MySQL在開始就定義好了。對運維人員來說,他們可能不清楚資料庫內部資料的資料格式,這也會資料庫的運維帶來了麻煩。

2. 測試目的

MongoDB與MySQL作為兩種不同型別的資料庫,當其中存放的記錄越來越多的時候,其插入效率將會受到怎樣的影響,是本次實驗所關注的物件。

在這裡,我們將本次實驗資料庫中資料儲存的規模定在1億條

3. 測試條件

機器配置: CPU:Intel(R) Xeon(R) CPU E5-2620 @ 2.00GHz

         記憶體:65954040 KB

         (關鍵詞:12CPU64G記憶體,給我多好)

作業系統: Linux version 2.6.32_1-8-0-0 (gcc version 4.4.4 20100726 (Red Hat 4.4.4-13) (GCC) )

MongoDB版本: 2.2.3,無任何優化配置,單機測試

MySQL版本: 5.1.49,無任何優化配置,單機測試

測試語言: Python 2.7.1

資料庫介面驅動:

MongoDB :   PyMongo  2.1.1

MySQL:   MySQLdb  1.2.3

4. 概念普及

在資料庫存放的資料中,有一種特殊的鍵值叫做主鍵,它用於惟一地標識表中的某一條記錄。也就是說,一個表不能有多個主鍵,並且主鍵不能為空值。

無論是MongoDB還是MySQL,都存在著主鍵的定義。

對於MongoDB來說,其主鍵名叫”_id”,在生成資料的時候,如果使用者不主動為其分配一個主鍵的話,MongoDB會自動為其生成一個隨機分配的值。

在MySQL中,主鍵的指定是在MySQL插入資料時指明PRIMARY KEY來定義的。當沒有指定主鍵的時候,另一種工具 —— 索引,相當於替代了主鍵的功能。索引可以為空,也可以有重複,另外有一種不允許重複的索引叫惟一索引。如果既沒有指定主鍵也沒有指定索引的話,MySQL會自動為資料建立一個。

5. 測試方法

1.       制定一個數據庫表項的欄位模板,以此模板為基準向資料庫中插入資料。

2.       在記憶體中自動生成1億條待測試資料。資料的格式這裡不再一一列出,其裡面包含了大概45個欄位,其中有一個關鍵的欄位是1 – 100,000,000 md5,它們彼此並不相同,其他欄位的資料都是寫死。每條資料的大小大概有1K

記住,本次測試的方法是先在記憶體中生成1億條資料後,再執行插入操作的。還好測試機器的記憶體足夠大,能夠存下如此多的資料。

3.       以如下四種模式向資料庫中插入資料,每插1000條資料時,就往一個固定的檔案中寫入該時刻的時間:

a)         在MongoDB中指定_id1 – 100,000,000 md5,將資料插入;

b)         在MongoDB中不指定_id值,將1 – 100,000,000 的md5值視為普通的欄位插入;

c)         在MySQL中1 – 100,000,000 md5值為PRIMARY KEY,將資料插入;

d)           在MySQL中不指定PRIMARY KEY,將1 – 100,000,000 的md5值視為普通的欄位插入。

4.       根據生成的時間檔案記錄,分析MySQL與MongoDB的插入效能。

5.       進一步,在以上四種資料庫的基礎上,再分別測試一下資料庫的讀取效能。

6. 測試過程

寫好測試指令碼之後,執行之,睡一覺起床來看結果就可以了。過程是漫長的,但結果卻是可喜可賀的 :D

7. 測試結果

7.1  平均每條資料的插入時間

先上張圖,來點直觀感受:

圖上資料橫座標是平均每插入1000條資料所需要的時間,單位是秒。記住,是每1000條資料,不是每條資料哦。

總結:

1.       資料庫的平均插入速率:MongoDB不指定_id插入 > MySQL不指定主鍵插入 > MySQL指定主鍵插入 > MongoDB指定_id插入

2.       MongoDB在指定_id與不指定_id插入時速度相差很大,而MySQL的差別卻小很多。

分析:

1.         在指定_id或主鍵時,兩種資料庫在插入時要對索引值進行處理,並查詢資料庫中是否存在相同的鍵值,這會減慢插入的速率。

2.         在MongoDB中,指定索引插入比不指定慢很多,這是因為,MongoDB裡每一條資料的_id都是唯一的。當在不指定_id插入資料的時候,其_id是系統自動計算生成的。MongoDB通過計算機特徵值、時間、程序ID與隨機數來確保生成的_id是唯一的。而在指定_id插入時,MongoDB每插一條資料,都需要檢查此_id可不可用,當資料庫中資料條數太多的時候,這一步的查詢開銷會拖慢整個資料庫的插入速度。

3.         MongoDB會充分使用系統記憶體作為快取,這是一種非常優秀的特性。我們的測試機的記憶體有64G,在插入時,MongoDB會盡可能地在記憶體快寫不進去資料之後,再將資料持久化儲存到硬碟上。這也是在不指定_id插入的時候,MongoDB的效率遙遙領先的原因。但在指定_id插入時,當資料量一大記憶體裝不下時,MongoDB就需要將磁碟中的資訊讀取到記憶體中來查重,這樣一來其插入效率反而慢了。

4.         MySQL不愧是一種非常穩定的資料庫,無論在指定主鍵還是在不指定主鍵插入的情況下,其效率都差不了太多。

7.2  插入穩定性分析

插入穩定性是指,隨著資料量的增大,每插入一定量資料時的插入速率情況。

在本次測試中,我們把這個指標的規模定在10w即顯示的資料是在每插入10w條資料時,在這段時間內每秒鐘能插入多少條資料

先呈現四張圖上來:

1.       MongoDB指定_id插入:

2.       MongoDB不指定_id插入:

3.       MySQL指定PRIMARY KEY插入:

4.       MySQL不指定PRIMARY KEY插入:

總結:

1.       整體上的插入速度還是和上一回的統計資料類似:MongoDB不指定_id插入 > MySQL不指定主鍵插入 > MySQL指定主鍵插入 > MongoDB指定_id插入

2.       從圖中可以看出,在指定主鍵插入資料的時候,MySQL與MongoDB在不同資料數量級時,每秒插入的資料每隔一段時間就會有一個波動,在圖表中顯示成為規律的毛刺現象。而在不指定插入資料時,在大多數情況下插入速率都比較平均,但隨著資料庫中資料的增多,插入的效率在某一時段有瞬間下降,隨即又會變穩定。

3.       整體上來看,MongoDB的速率波動比MySQL的嚴重,方差變化較大。

4.       MongoDB在指定_id插入時,當插入的資料變多之後,插入效率有明顯地下降。在其他三種的插入測試中,從開始到結束,其插入的速率在大多數的時候都固定在一個標準上。

分析:

1.       毛刺現象是因為,當插入的資料太多的時候,MongoDB需要將記憶體中的資料寫進硬碟,MySQL需要重新分表。這些操作每當資料庫中的資料達到一定量級後就會自動進行,因此每隔一段時間就會有一個明顯的毛刺。

2.       MongoDB畢竟還是新生事物,其穩定性沒有已應用多年的MySQL優秀。

3.       MongoDB指定_id插入的時候,其效能的下降還是很厲害的

7.3  MySQL與MongoDB讀取效能的簡單測試

這是一個附加的測試,也並沒有測試得非常完整,但還是很能說明一些問題的。

測試方法:

先在1 – 100, 000, 000這一億個數中,分別隨機取1w, 5w, 10w, 20w, 50w個互不相同的數字,再計算其md5,並儲存。

至於為什麼最高只選到50w這個規模,這是因為我在隨機生成100w個互不相同的數字的時候,寫的指令碼跑了一晚上都沒有跑出來,估計是我生成的演算法寫得太爛了。我不想重新再弄了,暫就以50w為上限吧。

在上述帶主鍵插入的兩個資料庫裡,分別以上一步生成的md5源為輸入進行查詢操作。同樣,每查詢1000條資料在日誌檔案中將當前系統時間寫入。

測試結果:

以下三張圖的橫座標是每查詢1000條資料所需要的時間,單位為s;縱座標是查詢的規模,分為1w, 5w,10w, 20w, 50w五個等級。

這張圖是詳細對比,可以看出MySQL與MongoDB之間的差異了嗎……

總結:

1.       在讀取的資料規模不大時,MongoDB的查詢速度真是一騎絕塵,甩開MySQL好遠好遠。

2.       在查詢的資料量逐漸增多的時候,MySQL的查詢速度是穩步下降的,而MongoDB的查詢速度卻有些起伏。

分析:

1.       如果MySQL沒有經過查詢優化的話,其查詢速度就不要跟MongoDB比了。MongoDB可以充分利用系統的記憶體資源,我們的測試機器記憶體是64GB的,記憶體越大MongoDB的查詢速度就越快,畢竟磁碟與記憶體的I/O效率不是一個量級的

2.       本次實驗的查詢的資料也是隨機生成的,因此所有待查詢的資料都存在MongoDB的記憶體快取中的概率是很小的。在查詢時,MongoDB需要多次將記憶體中的資料與磁碟進行互動以便查詢,因此其查詢速率取決於其互動的次數。這樣就存在這樣一種可能性,儘管待查詢的資料數目較多,但這段隨機生成的資料被MongoDB以較少的次數從磁碟中取出。因此,其查詢的平均速度反而更快一些。這樣看來,MongoDB的查詢速度波動也處在一個合理的範圍內。

3.       MySQL的穩定性還是毋庸置疑的。

8. 測試總結

8.1  測試結論

1. 相比較MySQL,MongoDB資料庫更適合那些讀作業較重的任務模型。MongoDB能充分利用機器的記憶體資源。如果機器的記憶體資源豐富的話,MongoDB的查詢效率會快很多。

2. 在帶”_id”插入資料的時候,MongoDB的插入效率其實並不高。如果想充分利用MongoDB效能的話,推薦採取不帶”_id”的插入方式,然後對相關欄位作索引來查詢

8.2  測試需要進一步注意的問題

對MongoDB的讀取測試考慮不周,雖然這只是一個額外的測試。在這個測試中,隨機生成大量待測試的資料很有必要,但生成大量互不相同的資料就沒有必要了。正是這一點,把我的讀取測試規模限定在了50w條,沒能進一步進行分析。

8.3  MongoDB的優勢

1. MongoDB適合那些對資料庫具體資料格式不明確或者資料庫資料格式經常變化的需求模型,而且對開發者十分友好

2. MongoDB官方就自帶一個分散式檔案系統,可以很方便地部署到伺服器機群上。MongoDB裡有一個Shard的概念,就是方便為了伺服器分片使用的。每增加一臺Shard,MongoDB的插入效能也會以接近倍數的方式增長,磁碟容量也很可以很方便地擴充。

3. MongoDB還自帶了對map-reduce運算框架的支援,這也很方便進行資料的統計。

其他方面的優勢還在發掘中,本人也是剛剛接觸這個不久。

8.4  MongoDB的缺陷

1. 事務關係支援薄弱。這也是所有NoSQL資料庫共同的缺陷,不過NoSQL並不是為了事務關係而設計的,具體應用還是很需求。

2. 穩定性有些欠缺,這點從上面的測試便可以看出。

3. MongoDB一方面在方便開發者的同時,另一方面對運維人員卻提出了相當多的要求。業界並沒有成熟的MongoDB運維經驗,MongoDB中資料的存放格式也很隨意,等等問題都對運維人員的考驗。