MongoDB Note 01

基礎概念簡介

前言

傳統的資料庫使用的是關聯式資料庫管理系統(Relational Database Management System/RDBMS),需要在關聯式資料庫中事先定義好Schema(綱要);也就是每張Table(資料表)中包含哪一些Column(欄位),並且要確保後續新增儲存的Row(資料)都要遵循schema的定義。

這邊舉個例子,如果想要在RDBMS中記錄會員的資訊,如果最初定義Product 資料表的schema中有姓名、職業、身高、體重四個欄位,則該資料表中的每一筆資料都必須要有這四個欄位,結果資料表快建完了才發現忘記加上血型這個欄位,又或者希望幫其中一個會員紀錄上興趣,這種狀況在關聯式資料庫中,因為要遵守schema的定義,所以需要更動schema,在欄位上新增血型、興趣欄位,再來寫一個script幫現有所有的會員都加上興趣(若無可填上空值)。

比起上述的情況,對於MongoDB來說,因為它被設計為一種以document為主的database,所以可以直接將興趣欄位直接加入屬於這個會員的資料內即可。除了可以很容易的增加欄位外,MongoDB還能透過Sharding功能,輕易地水平擴展整體的資料庫大小。

💡
因為MongoDB不需要定義Schema (Schemaless),可以直接新增,如此可以大幅縮短開發時間,適合用於資料格式常變動的應用中。

MongoDB的層級架構

MongoDB的層級從大到小分別是DatabaseCollectionDocument,下面這張圖對應了MongoDB和傳統關聯式資料庫SQLDatabase的對應關係:

SQL MongoDB 說明
Database Database 資料庫
Table Collection 資料表/集合
Row Document 一筆數據紀錄/文檔
Column Field 數據欄位/域
Index Index 索引
Table Joins 合併資料表/MongoDB沒有Join
Primary Key Primary Key 主鍵

Database

一個MongoDB伺服器中可以有多個獨立的Database,每一個都有自己的collection和權限,在實際上的應用中,不同服務或是應用的資料會放在不同的database中。

例如有一家公司同時有A和B服務,兩者的資料是不相通的,那就可以直接在MongoDB中建立兩個database,分別存放兩服務所需要儲存的資料。

資料庫的名字在MongoDB中有著一些限制:

  1. 不能是空字串
  2. 不可以含有' '(空格)、.(點)、$(錢字符)、/(斜線)、\(反斜線)和\0(空字符)
  3. 應全為小寫
  4. 最多64個字元

此外,有一些database的名稱是建立時就預設存在的,可以直接連結進行查看:

  • admin
    MongoDB會將所有使用者的資訊儲存在這個database(名稱、密碼和使用者的驗證資料庫),如果要建立個別database的使用者,在個別的database中建立即可,但如果要跨database使用,則需要在這個資料庫中建立,會自動繼承到其他database,關於管理使用者的方法會在額外發一篇介紹。
  • local
    這個database永遠不會被複製,用來儲存這台機器的檔案和資訊,不會因為建立💡replica set(建立副本,預防MongoDB掛掉而導致服務掛掉)複製這個database的資料。
  • config
    主要存放Sharding相關的資料,在3.6版本以後,會儲存standalone或是replication資料,也有跟transaction相關的資料,但是禁止去針對這個database進行修改或刪除,僅可以查看。
    當MongoDB使用Sharding時,config database會在內部使用,用於保存sharding的相關資訊。
💡
Replica Set為近年多數資料庫所使用的架構,以Primary和Secondary來取代舊有的Master和Slave架構,具有自動故障恢復、讀寫控制等等優勢。

建立database的方法很簡單,直接使用use即可切換到指定的database,如果在MongoDB中沒有該database則會直接建立一個新的:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> use testDataset
switched to db testDataset

Collection

Collection相當於關聯式資料庫的 Table的概念,主要差別在它存放的是document而非固定大小、格式的record,通常一個 databse 中會有數個 collection。

以database上的例子來說如果服務 A為一個銷售資料庫,可能會想要分別記錄「購買人資訊」、「製造商資訊」、「販售地點」...等等,因此可以在「產品 A 的 database」 中分別開設對應的 collection。

同時因為MongoDB沒有固定的結構,所以還可以針對特定的資料設計不同的結構,但同時都是屬於同一個collection。

例如以「購買人資訊」為例,可以只針對特定的user增加興趣的欄位,而其他的user則完全不受影響:

{
    "name": 'Stank',
    "job": 'software engineer',
    "bloodType": 'A'
}
{
    "name": 'Augustus',
    "job": 'software engineer',
    "bloodType": 'O',
    "hobbies": ['sleeping', 'computer game', 'guitar'],
}

Document

Document就是一筆資料,概念好比關聯式資料庫的Row。通常一個collection中會有數筆document。也因此我們稱MongoDB是document database

例如在「購買人資訊」的collection中,真正的一筆「購買人資訊」就是一個document。

以下是Document的例子,可以看到長的跟JSON一樣,由多組的key-value組成,不過在MongoDB中是使用field這個名稱:

{
    "name": "Augustus",               <----field:value
    "age": 26,                        <----field:value
    "status": "S",                    <----field:value
    "groups": ["MongoDB", "GitLab"]   <----field:value
}

MongoDB儲存格式 (BSON)

MongoDB每筆document都是像JSON格式的key-value組合,但實際上MongoDB在儲存資料的格式是BSON(Binary JSON,JSON二進位表示形式),使用BSON的優點在於在空間上儲存較有效率,BSON支援的格式可以參考官網,這裡介紹一些比較特別的格式:

  • binData
    儲存二進制的資料(binary data)。
  • timestamp & date
    timestamp和date一樣是儲存時間的資料,但是date在跨時區的時候會有需要轉換時區的問題產生。
  • object
    可以在這個欄位中插入document,可以想像成文件中的子文件,如下範例的livein欄位:
{
    "name": 'Augustus',
    "job": 'software engineer',
    "bloodType": 'O',
    "hobbies": ['sleeping', 'computer game', 'guitar'],
    "livein":
    {
        "contry": 'Taiwan',
        "city": 'Taipei'
    }
}
  • regex
    用來儲存正則表達式。
  • javascript
    用來儲存javascript語法。
  • objectId
    會在文章下面提到。

objectId

MongoDB的核心,可以透過這個id直接處理document,以下是objectId的幾個特性:

  • 預設每一個文件都有的欄位(連object型態的資料都會有)
  • 如果沒有設定,會自動產生這個欄位
  • 每一個文件的唯一位置
  • 系統預設的single index key
  • collection層級的唯一值
  • 資料行別為ObjectId

以下是一個objectId的例子:

db.costumer.insertOne({ ... })

db.costumer.find()

{
    "_id" : ObjectId("62d511895ddbc92df38d37e3"),
    "name": 'Augustus',
    "job": 'software engineer',
    "bloodType": 'O',
    "hobbies": ['sleeping', 'computer game', 'guitar'],
    "livein":
    {
        "contry": 'Taiwan',
        "city": 'Taipei'
    }
}
    

系統如何生成objectId的相關文檔在這可以看到,另外objectId也可以自行定義,其他型別也可以拿來做為_id,但是需要確保_id在collection層級上的唯一性。

MongoDB優缺點比較

優點 缺點
靈活的數據模型 需要更多的儲存空間
大部分可工作的數據儲存在RAM RAM的使用會爆高
可水平擴展 (Sharding支援20PB) 弱一致性 (最終一致),沒有💡ACID保證
沒有Triger
文檔=物件,不需要ORM(物件關係對映) 重複的數據
索引(index) 索引(index)
不支援Join
💡
ACID,是指資料庫管理系統(DBMS)在寫入或更新資料的過程中,為保證事務(transaction)是正確可靠的,所必須具備的四個特性:原子性(atomicity,或稱不可分割性)、一致性(consistency)、隔離性(isolation,又稱獨立性)、持久性(durability)。

MongoDB實務層面上的問題

在使用MongoDB之前有查詢到MongoDB在實際使用上的一些問題:

  1. Aggregation查詢在橫跨多個Database、多個Collection時會變慢很多
    需要有設計良好的Database和Collection才能確保擁有快速的查詢速度
  2. 後期效能問題:
    百G以上的資料,需要有良好的index設計來提速
    ex.資料庫內有十萬條6000字文本後,查詢速度降低

結論

MongoDB強勢的地方在於儲存資料的格式彈性,由於屬於NoSQL不需要定義Schema,可以輕易的新增、修改資料,對於剛開始使用資料庫,資料的格式還沒定下來的應用十分友善,在效能方面也是相當快速,又因為可以快速的水平擴張,在大數據的應用上也是相當方便。