什麼是MongoDB ?
MongoDB 是一個開源的文檔資料庫,它基于 C++ 語言編寫,性能高,可用性強,能夠自動擴充。
MongoDB 是最流行的 NoSQL 資料庫之一,原生支援分布式叢集架構,特别适合處理大資料,阿裡巴巴、騰訊、頭條、Twitter、Google、Facebook 等一線網際網路公司都在使用 MongoDB 資料庫。
與 HBase 相比,MongoDB 可以存儲具有更加複雜的資料結構的資料,具有很強的資料描述能力。MongoDB 提供了豐富的操作功能,但是它沒有類似于 SQL 的操作語言,文法規則相對比較複雜。
MongoDB(來自英文單詞“Humongous”,中文含義為“龐大”)是可以應用于各種規模的企業、各個行業以及各類應用程式的開源資料庫。
MongoDB的優勢
MongoDB 使用廣泛
MongoDB 是目前 NoSQL 資料庫中使用最廣泛的資料庫之一,根據 DB-Engines 2020 年 9 月份釋出的全球資料庫排名(見圖 1),前五名依次是 Oracle、MySQL、Microsoft SQL Server、PostgreSQL、MongoDB ,此排名順序已經持續很長時間,MongoDB 排名第五,9月份 MongoDB 的分數依然保持增長,而且還是整個排行榜中增長幅度最大的一個。
MongoDB 性能高
MongoDB 是一個開源文檔資料庫,是用 C++ 語言編寫的非關系型資料庫。其特點是高性能、高可用、可伸縮、易部署、易使用,存儲資料十分友善,主要特性有:面向集合存儲,易于存儲對象類型的資料,模式自由,支援動态查詢,支援完全索引,支援複制和故障恢複,使用高效的二進制資料存儲,檔案存儲格式為 BSON ( 一種 JSON 的擴充)等。
MongoDB 提供高性能資料讀寫功能,并且性能還在不斷地提升。根據官方提供的 MongoDB 3.0 性能測試報告,在 YCSB 測試中,MongoDB 3.0 在多線程、批量插入場景下的處理速度比 MongoDB 2.6 快 7 倍。
關于讀寫與響應時間的具體測試結果參見圖 2。
圖 2:MongoDB 2.6 與 3.0 讀寫性能與響應時間性能測試
MongoDB 支援分布式
在生産過程中,因機器故障導緻系統當機的問題不可避免;集中式系統在計算能力和存儲能力方面的瓶頸,也無法滿足目前的資料量爆發式增長的需求。這兩個問題就是系統對高可用和可伸縮架構的需求,MongoDB 在原生上就可滿足這兩方面的需求。
MongoDB 的高可用性展現在對副本集 Replication 的支援上,可伸縮性展現在分片叢集的部署方式上。
MongoDB 的 Replication 集提供自動故障轉移和資料備援服務,Replication 結構可以保證資料庫中的全部資料都會有多份備份,這與 HDFS 分布式檔案系統的備份機制比較類似。采用副本集的叢集中具有主(Master)、從(Slaver)、仲裁(Arbiter)三種角色。
主從關系(Master-Slaver) 負責資料的同步和讀寫分離;Arbiter 服務負責心跳(Heartbeat)監控,Master 當機時可将 Slaver 切換到 Mas 血狀态,繼續提供資料的服務,完成了資料的高可用需求。
當需要存儲大量的資料時,主從伺服器都需要存儲全部資料,可能會出現寫性能問題。同時, Replication 主要解決的是讀資料高可用方面的問題,在對資料庫查詢時也隻限制在一台伺服器上, 并不能支援一次查詢多台資料庫伺服器,并沒有滿足資料庫讀寫操作的分布式需求。
MongoDB 提供水準可伸縮性功能的是分片(Shard)。分片與在 HDFS 分布式檔案系統中上傳檔案會将檔案切成 128MB(Hadoop2.x 預設配置)相似,通過将資料切成數片(Sharding)寫入不同的分片節點,完成分布式寫的操作。同時,MongoDB 在讀取時提供了分布式讀的操作,這個功能與 HDFS 的分布式讀寫十分類似。
MongoDB 便于開發
MongoDB 對開發者十分友好,便于使用。支援豐富的查詢語言、資料聚合、文本搜尋和地理空間查詢,使用者可以建立豐富的索引來提升查詢速度,MongoDB 被稱為最像關系資料庫的非關系資料庫。
MongoDB 允許使用者在服務端執行腳本,可以用 Javascript 編寫某個函數,直接在服務端執行,也可以把函數的定義存儲在服務端,使用時直接調用即可。MongoDB 支援各種程式設計語言,包括 Ruby、Python、Java、C++、PHP、C# 等。
Robomongo(MongoDB可視化工具)簡介
Robomongo 是一個界面友好且免費的 MongoDB 可視化工具,可在 Robomongo 官網下載下傳此軟體,其安裝過程十分簡單,安裝好的界面如下圖所示。
在 MongoDB Connections 視窗單擊滑鼠右鍵添加 MongoDB 資料庫,設定如下圖所示。
連接配接成功後,MongoDB 中所有資料庫以及集合均顯示在左側導航欄,如圖下所示。
從上圖中可以看到 Robomango 提供可視化的界面将資料庫中的文檔顯示出來,在集合上單擊滑鼠右鍵可以顯示提供的集合操作。
使用 Robomango,初學者能更容易了解 MongoDB 資料庫的概念。
MongoDB的文檔資料模型
傳統的文檔資料庫(Document Storage)概念的提岀要追溯到 1989 年,Lotus 提出的 Notes 産品被稱為文檔資料庫,這種文檔資料庫常用于管理文檔,如 Word、建立工作流任務等。
文檔資料庫差別于傳統的其他資料庫,它可用來管理文檔,尤其擅長處理各種非結構化的文檔資料。在傳統的資料庫中,資訊被分割成離散的資料段,而在文檔資料庫中,文檔是處理資訊的基本機關。
傳統的文檔資料庫與 20 世紀 50 ~ 60 年代管理資料的檔案系統不同,文檔資料庫仍屬于資料庫範疇。
首先,檔案系統中的檔案基本上對應于某個應用程式。當不同的應用程式所需要的資料部分相同時,也必須建立各自的檔案,而不能共享資料,而文檔資料庫可以共享相同的資料。是以,檔案系統比文檔資料庫資料備援度更大,更浪費存儲空間,且更難于管理維護。
其次,檔案系統中的檔案是為某一特定應用服務的,是以,要想對現有的資料再增加一些新的應用是很困難的,系統難以擴充,資料和程式缺乏獨立性。而文檔資料庫具有資料的實體獨立性和邏輯獨立性,資料和程式分離。
NoSQL 中的文檔資料庫
NoSQL 中的文檔資料庫(以下文檔資料庫均指 NoSQL 中的文檔資料庫)與傳統的文檔資料庫不是同一種産品,NoSQL 中的文檔資料庫(MongoDB)有自己特定的資料存儲結構及操作要求。
在傳統資料庫的發展過程中,基本都是出現一種資料模型,再依據資料模型,開發出相關的資料庫,例如,層次資料庫是建立在層次資料模型的基礎上,關系資料庫是建立在關系資料模型的基礎上的。NoSQL 中文檔資料庫的出現也是建立在文檔資料模型的基礎上的。
NoSQL 中的文檔資料庫與傳統的關系資料庫均建立在對磁盤讀寫的基礎上,實作對資料的各種操作。文檔資料庫的設計思路是盡可能地提升資料的讀寫性能,為此選擇性地保留了部分關系型資料庫的限制,通過減少讀寫過程的規則限制,提升了讀寫性能。
MongoDB 文檔資料模型
傳統的關系型資料庫需要對表結構進行預先定義和嚴格的要求,而這樣的嚴格要求,導緻了處理資料的過程更加煩瑣,甚至降低了執行效率。
在資料量達到一定規模的情況下,傳統關系型資料庫反應遲鈍,想解決這個問題就需要反其道而行之,盡可能去掉傳統關系型資料庫的各種規範限制,甚至事先無須定義資料存儲結構。
文檔存儲支援對結構化資料的通路,與關系模型不同的是,文檔存儲沒有強制的架構。文檔存儲以封包鍵值對的方式進行存儲,文檔存儲模型支援嵌套結構。
- 例如,文檔存儲模型支援 XML 和 JSON 文檔,字段的“值”可以嵌套存儲其他文檔,也可存儲數組等複雜資料類型。
MongoDB 存儲的資料類型為 BSON,BSON 與 JSON 比較相似,文檔存儲模型也支援數組和鍵值對。
MongoDB 的文檔資料模型如圖下所示,MongoDB 的存儲邏輯結構為文檔,文檔中采用鍵值對結構,文檔中的 _id 為主鍵,預設建立主鍵索引。從 MongoDB 的邏輯結構可以看出,MongoDB 的相關操作大多通過指定鍵完成對值的操作。
文檔資料庫無須事先定義資料存儲結構,這與鍵值資料庫和列族資料庫類似,隻需在存儲時采用指定的文檔結構即可。從上圖可以看出,一個
{}
中包含了若幹個鍵值對,大括号中的内容就被稱為一條文檔。
MongoDB的文檔存儲結構
MongoDB 文檔資料庫的存儲結構分為四個層次,從小到大依次是:鍵值對、文檔(document)、集合(collection)、資料庫(database)。
圖 1 描述了 MongoDB 的存儲與 MySQL 存儲的對應關系,可以看出,MongoDB中的文檔、集合、資料庫對應于關系資料庫中的行資料、表、資料庫。
圖 1:MongoDB 存儲與 Mysql 存儲的對比
鍵值對
文檔資料庫存儲結構的基本機關是鍵值對,具體包含資料和類型。鍵值對的資料包含鍵和值,鍵的格式一般為字元串,值的格式可以包含字元串、數值、數組、文檔等類型。
按照鍵值對的複雜程度,可以将鍵值對分為基本鍵值對和嵌套鍵值對。
- 圖 2 中的鍵值對中的鍵為字元串,值為基本類型,這種鍵值對就稱為基本鍵值。
- 嵌套鍵值對類型如圖 3 所示,從圖中可以看岀, contact 的鍵對應的值為一個文檔,文檔中又包含了相關的鍵值對,這種類型的鍵值對稱為嵌套鍵值對。
圖 2:MongoDB 文檔資料模型
圖 3:嵌套鍵值對
鍵(Key)起唯一索引的作用,確定一個鍵值結構裡資料記錄的唯一性,同時也具有資訊記錄的作用。例如,country:“China”,用
:
實作了對一條位址的分割記錄,“country”起到了 “China”的唯一位址作用,另外,“country”作為鍵的内容說明了所對應内容的一些資訊。
值(Value)是鍵所對應的資料,其内容通過鍵來擷取,可存儲任何類型的資料,甚至可以為空。
鍵和值的組成就構成了鍵值對(Key-Value Pair)。它們之間的關系是一一對應的,如定義了 “country:China”鍵值對,"country”就隻能對應“China”,而不能對應“USA”。
文檔中鍵的命名規則如下。
- UTF-8 格式字元串。
- 不用有
的字元串,習慣上不用\0
和.
。$
- 以開頭的多為保留鍵,自定義時一般不以開頭。
- 文檔鍵值對是有序的,MongoDB 中嚴格區分大小寫。
文檔
文檔是 MongoDB 的核心概念,是資料的基本單元,與關系資料庫中的行十分類似,但是比行要複雜。文檔是一組有序的鍵值對集合。文檔的資料結構與 JSON 基本相同,所有存儲在集合中的資料都是 BSON 格式。
BSON 是一種類 JSON 的二進制存儲格式,是 Binary JSON 的簡稱。 一個簡單的文檔例子如下:
MongoDB 中的資料具有靈活的架構,集合不強制要求文檔結構。但資料模組化的不同可能會影響程式性能和資料庫容量。文檔之間的關系是資料模組化需要考慮的重要因素。文檔與文檔之間 的關系包括嵌入和引用兩種。
下面舉一個關于顧客 patron 和位址 address 之間的例子,來說明在某些情況下,嵌入優于引用。
{
_id: "joe",
name: "Joe Bookreader"
}
{
patron_id: "joe",
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "2345"
}
關系資料庫的資料模型在設計時,将 patron 和 address 分到兩個表中,在查詢時進行關聯, 這就是引用的使用方式。如果在實際查詢中,需要頻繁地通過 _id 獲得 address 資訊,那麼就需要頻繁地通過關聯引用來傳回查詢結果。在這種情況下,一個更合适的資料模型就是嵌入。
将 address 資訊嵌入 patron 資訊中,這樣通過一次查詢就可獲得完整的 patron 和 address 資訊,如下所示:
{
_id: "joe",
name: "Joe Bookreader",
address: {
street: "123 Fake Street",
city: "Faketon”,
state: nMAnz
zip: T2345”
}
}
如果具有多個 address,可以将其嵌入 patron 中,通過一次查詢就可獲得完整的 patron 和多個 address 資訊,如下所示:
{
_id: "joe",
name: "Joe Bookreader",
addresses:[
{
street: "123 Fake Streetn,
city: "Faketon",
state: "MA",
zip: "12345"
},
{
street: "l Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
]
}
但在某種情況下,引用比嵌入更有優勢。下面舉一個圖書出版商與圖書資訊的例子,代碼如下:
{
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolfn"],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher: {
name: "O'Reilly Media",
founded: 1980,
location: "CA"
}
}
{
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English",
publisher: {
name: "O'Reilly Media",
founded: 1980,
location: "CA"
}
}
從上邊例子可以看出,嵌入式的關系導緻出版商的資訊重複釋出,這時可采用引用的方式描述集合之間的關系。使用引用時,關系的增長速度決定了引用的存儲位置。如果每個出版商的圖書數量很少且增長有限,那麼将圖書資訊存儲在出版商文檔中是可行的。
通過 books 存儲每本圖書的 id 資訊,就可以查詢到指定圖書出版商的指定圖書資訊,但如果圖書出版商的圖書數量很多, 則此資料模型将導緻可變的、不斷增長的數組 books,如下所示:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [123456789, 234567890, …]
}
{
_id: 123456789,
title: "MongoDE: The Definitive Guide",
author: ["Kristina Chodorow", "Mike Dirolf"],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}
為了避免可變的、不斷增長的數組,可以将出版商引用存放到圖書文檔中,如下所示:
{
_id: "oreilly",
name: "O'Reilly Media",
founded: 1980,
location: "CA"
}
{
_id: 123456789,
title: "MongoDB: The Definitive Guiden,
author: [ "Kristina Chodorow", "Mike Dirolf"],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher_id: "oreilly"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published date: ISODate("2011-05-06"),
pages: 68,
language: "English",
publisher_id: "oreilly"
}
集合
MongoDB 将文檔存儲在集合中,一個集合是一些文檔構成的對象。如果說 MongoDB 中的文檔類似于關系型資料庫中的“行”,那麼集合就如同“表”。
集合存在于資料庫中,沒有固定的結構,這意味着使用者對集合可以插入不同格式和類型的資料。但通常情況下插入集合的資料都會有一定的關聯性,即一個集合中的文檔應該具有相關性。
集合的結構如圖 4 所示。
圖 4:文檔資料庫中的一個集合
資料庫
在 MongoDB 中,資料庫由集合組成。一個 MongoDB 執行個體可承載多個資料庫,互相之間彼此獨立,在開發過程中,通常将一個應用的所有資料存儲到同一個資料庫中,MongoDB 将不同資料庫存放在不同檔案中。
資料庫結構示例如圖 5 所示。
圖 5:一個名為 DB 的資料庫的結構
BSON對JSON做了哪些改進?
我們說,MongoDB 存儲的資料格式與 JSON 十分類似,MongoDB 所采用的資料格式被稱為 BSON,是一種基于 JSON 的二進制序列化格式,用于 MongoDB 存儲文檔并進行遠端過程調用。
JSON 是一種網絡常用的資料格式,具有自描述性。JSON 的資料表示方式易于解析,但支援的資料類型有限。BSON 目前主要用于 MongoDB 中,選擇 JSON 進行改造的原因主要是 JSON 的通用性及 JSON 的 schemaless 的特性。
BSON 改進的主要特性有下面三點。
更快的周遊速度
BSON 對 JSON 的一個主要的改進是,在 BSON 元素的頭部有一個區域用來存儲元素的長度, 當周遊時,如果想跳過某個文檔進行讀取,就可以先讀取存儲在 BSON 元素頭部的元素的長度, 直接 seek 到指定的點上就完成了文檔的跳過。
在 JSON 中,要跳過一個文檔進行資料讀取,需要在對此文檔進行掃描的同時比對資料結構才可以完成跳過操作。
操作更簡易
如果要修改 JSON 中的一個值,如将 9 修改為 10,這實際是将一個字元變成了兩個,會導緻其後面的所有内容都向後移一位。
在 BSON 中,可以指定這個列為整型,那麼,當将 9 修正為 10 時,隻是在整型範圍内将數字進行修改,資料總長不會變化。
需要注意的是:如果數字從整型增大到長整型,還是會導緻資料總長增加。
支援更多的資料類型
BSON 在 JSON 的基礎上增加了很多額外的類型,BSON 增加了“byte array”資料類型。這使得二進制的存儲不再需要先進行 base64 轉換再存為 JSON,減少了計算開銷。
BSON 支援的資料類型如表所示。
類型 | 描述示例 |
---|---|
NULL | 表示空值或者不存在的字段,{“x” : null} |
Boolean | 布爾型有 true 和 false,{“x” : true} |
Number | 數值:用戶端預設使用 64 位浮點型數值。{“x” : 3.14} 或 {“x” : 3}。對于整型值,包括 NumberInt(4 位元組符号整數)或 NumberLong(8 位元組符号整數),使用者可以指定數值類型,{“x” : NumberInt(“3”)} |
String | 字元串:BSON 字元串是 UTF-8,{“x” : “中文”} |
Regular Expression | 正規表達式:文法與 JavaScript 的正規表達式相同,{“x” : /[cba]/} |
Array | 數組:使用“[]”表示,{“x” : [“a”, “b”, “c”]} |
Object | 内嵌文檔:文檔的值是嵌套文檔,{“a” : {“b” : 3}} |
ObjectId | 對象 id:對象 id 是一個 12 位元組的字元串,是文檔的唯一辨別,{“x” : objectId()} |
BinaryData | 二進制資料:二進制資料是一個任意位元組的字元串。它不能直接在 Shell 中使用。如果要将非 UTF-8 字元儲存到資料庫中,二進制資料是唯一的方式 |
JavaScript | 代碼:查詢和文檔中可以包括任何 JavaScript 代碼,{“x” : function(){/…/}} |
Data | 日期:{“x” : new Date()} |
Timestamp | 時間戳:var a = new Timestamp() |