對於開發者而言,數據庫的選擇往往決定了應用程序的架構靈活性。不少開發者應該都熟悉傳統的關係型數據庫(如 MySQL 或 PostgreSQL),初次接觸 MongoDB 時可能會感到一種思維模式的轉變。
今天一起來探討 MongoDB 為何被設計成現在的樣子,以及如何進行基礎的增刪改查(CRUD)操作。
關係型數據庫的世界
在關係型數據庫中,數據被整齊地排列在行和列中,就跟 Excel 表格似的。表結構(Schema)在數據寫入前就必須定義好。每一行都要遵循這個規則。
這種設計能確保數據的一致性和邏輯嚴密性。但在面對快速迭代的現代應用時,這種剛性可能成為瓶頸。如果業務需求變更,需要給現有的億級大表增加一個字段,那遷移工作就複雜多了,還有潛在的停機風險。
NoSQL = Not Only SQL
NoSQL 並不是不要SQL,我覺得它更傾向於 Not Only SQL。
並不是説要拋棄關係型數據庫,而是在工具箱中增加一個新的選項。對於社交網絡、遊戲日誌、物聯網數據等非結構化或半結構化數據,我們需要一種更靈活的存儲方式。MongoDB 正是其中的佼佼者,屬於文檔型數據庫(Document-Oriented) 。
核心概念:文檔與集合
MongoDB 不再將數據拆散存儲在不同的行和列中,而是將一個完整的對象存儲為一個文檔(Document) 。
文檔 (Document)
文檔是 MongoDB 的最小數據單元。它看起來非常像 JSON 對象,但技術上,MongoDB 使用的是 BSON(Binary JSON)。BSON 是一種二進制格式,讀寫速度更快,且支持 JSON 無法表達的數據類型(如日期、二進制數據等)。
文檔強就強在它能嵌套。開發者可以將列表、對象直接存入一個字段中,而不需要像 SQL 那樣建立多張關聯表。
代碼示例:一個電商訂單文檔
以下代碼展示了一個包含嵌套對象(收貨地址)和數組(商品列表)的文檔結構,這在 MongoDB 中是標準寫法:
{
// 唯一標識符,相當於主鍵
"_id": ObjectId("64b8f1e2a9b3c5d6e7f8a9b0"),
// 支持存儲具體的日期類型
"created_at": ISODate("2024-05-20T09:30:00Z"),
"status": "Processing",
// 嵌套對象:無需拆分到另一張表
"delivery_info": {
"Country": "France",
"city": "Paris",
"detail": "Avenue des Champs Elysées"
},
// 數組結構:直接存儲列表數據
"cart_items": [
{
"sku": "KB-MECHANICAL-01",
"count": 1,
"unit_price": 599.00
},
{
"sku": "MOUSE-PAD-XL",
"count": 2,
"unit_price": 49.50
}
]
}
集合 (Collection)
如果説文檔對應 SQL 中的“行”,那麼集合(Collection)就對應 SQL 中的“表”。
最大的區別是它的動態模式(Dynamic Schema) 。在同一個集合中,文檔的結構可以不完全相同。例如,在 users 集合中,有的用户可能有 wechat_id 字段,而有的用户只有 email 字段。這種靈活性讓數據建模更貼近面向對象編程的邏輯。
核心字段:_id
每個文檔都必須有一個 _id 字段,作為主鍵。如果在插入數據時沒有指定,MongoDB 會自動生成一個全局唯一的 ObjectId。這個 ID 包含了時間戳信息,因此甚至可以從 ID 中推算出數據的創建時間。
-
- *
基礎實戰:CRUD 操作
假設我們已經連接到了數據庫(使用 mongosh 或圖形化工具),下面演示如何對一個名為 employees的集合進行操作。
新增 (Create)
使用 insertOne 插入單條數據,或 insertMany 插入多條數據。
- 插入單條數據
// 向 employees 集合中添加一名新員工
db.employees.insertOne({
name: "Jack",
department: "Engineering",
is_manager: false,
skills: ["Java", "Docker"],
onboard_date: new Date()
})
- 插入多條數據
// 一次性添加多名實習生
db.employees.insertMany([
{
name: "Mike",
role: "Intern",
age: 21,
mentor: "Jack"
},
{
name: "Tommy",
role: "Intern",
age: 22,
mentor: "Jack"
}
])
查詢 (Read)
- 查詢單條 (
findOne) :返回匹配條件的第一條數據。常用於根據 ID 或唯一字段查找。
// 查找名字是 'Jack' 的員工
db.employees.findOne({ name: 'Jack' })
- 查詢多條 (
find) :返回所有匹配的數據。支持複雜的篩選操作符。
// 查詢所有年齡大於 21 歲的員工
// $gt 表示 greater than (大於)
db.employees.find({ age: { $gt: 21 } })
更新 (Update)
更新操作通常包含兩部分:篩選條件和更新動作(例如 $set)。
- 更新單條 (
updateOne)
// 找到名字是 'Tommy' 的員工,將其職位修改為 'Junior Developer'
db.employees.updateOne(
{ name: 'Tommy' }, // 篩選條件
{ $set: { role: 'Junior Developer' } } // 更新動作
)
- 更新多條 (
updateMany)
// 為所有沒有 'location' 字段的員工,添加默認辦公地點
// $exists: false 用於判斷字段不存在
db.employees.updateMany(
{ location: { $exists: false } },
{
$set: {
"location": "New York",
"wfh_allowed": true
}
}
);
刪除 (Delete)
- 刪除單條 (
deleteOne)
// 刪除第一個匹配到的名字為 'Mike' 的記錄
db.employees.deleteOne({ name: "Mike" })
- 刪除多條 (
deleteMany)
// 刪除所有實習生記錄
db.employees.deleteMany({ role: "Intern" });
注意:如果執行 db.employees.deleteMany({})(空篩選條件),將會清空整個集合,操作時需格外謹慎。
-
- *
搭建高效的本地開發環境
理解了理論和基礎指令後,下一步就是在本地環境中進行實戰演練。
對於開發者來説,維護本地數據庫環境比較繁瑣。可能需要安裝 Docker,編寫複雜的 Compose 文件,或者在系統中通過命令行安裝不同版本的數據庫,還要處理端口衝突和版本依賴問題。
但這些問題在ServBay面前都是小Case,因為ServBay 能夠極大簡化這一流程。它不僅支持一鍵安裝 MongoDB,更解決了多版本共存的痛點。
ServBay 的核心優勢:
- 多版本同時運行:ServBay支持運行 MongoDB 5.0 到 8.0 的實例,互不干擾。這對於維護不同歷史時期的項目非常方便,無需反覆卸載重裝。
- 全棧數據庫支持:除了 MongoDB,ServBay 還囊括了 MySQL、PostgreSQL、MariaDB 以及 Redis、Memcached 等主流 NoSQL 數據庫。
- 實例隔離:支持同時運行多個不同類型的數據庫實例,為每個項目提供獨立的沙盒環境。
- 極簡配置:告別繁雜的配置文件,通過直觀的圖形界面即可管理服務狀態和端口。
如果你希望跳過繁瑣的環境配置,直接進入代碼開發和數據庫結構設計的核心環節,ServBay 會是一個得力的助手。它讓數據庫的安裝和管理變得像手機安裝 App 一樣簡單,讓你能專注於構建更優秀的應用程序。