目錄
一、概念
二、架構
2.1 邏輯結構
2.2 數據模型
2.3 存儲引擎:WiredTiger?
三、事務
一、概念
?MongoDB是文檔數據庫,基本存儲單元是?文檔(Document),以BSON格式(一種類json的二進制形式)存儲,文檔可以包含鍵值對、嵌套文檔、數組等復雜結構,字段可以動態擴展,需要在代碼層管理數據約束,適合存儲非結構化數據或半結構化數據(如日志、IoT 傳感器數據、實時分析)。
文檔結構示例:
{"_id": ObjectId("507f191e810c19729de860ea"), // MongoDB 自動生成的唯一主鍵"name": "Alice Smith","age": 30,"email": "alice@example.com","address": {"street": "123 Main St","city": "Anytown","zip": "12345"},"hobbies": ["reading", "hiking", "photography"],"created_at": ISODate("2023-10-27T10:00:00Z")
}
設計目標:是解決關系型數據庫在處理海量數據、高并發、靈活數據結構時遇到的擴展性、性能瓶頸和模式僵化問題。
MongoDB與關系型數據庫RDBMS對比:
文檔 (Document):?數據存儲和操作的基本單元,類似于 JSON 對象,結構是鍵值對。
集合 (Collection):一組相關文檔的容器,同一個集合(Collection)中的文檔不要求具有相同的結構(模式自由/動態模式)。字段可以動態添加、修改或刪除。
數據庫 (Database):包含多個集合的物理容器,用于邏輯上隔離不同的應用或數據集,每個數據庫有自己的用戶、權限、集合和索引。
?MongoDB中Document中可以出現的數據類型:
二、架構
2.1 邏輯結構
MongoDB與MySQL架構相差不多,底層都使?了可插拔的存儲引擎以滿用戶的不同需要。最新版本的 MongoDB 中使用了 WiredTiger 作為默認的存儲引擎,WiredTiger 提供了不同粒度的并發控制和壓縮機制,能夠為不同種類的應用提供了最好的性能和存儲率。
在存儲引擎上層的就是 MongoDB 的數據模型和查詢語言了,由于 MongoDB 對數據的存儲與 RDBMS 有較大的差異,所以它創建了一套不同的數據模型和查詢語言。
2.2 數據模型
MongoDB的數據模型有兩種
1> 內嵌:把相關聯的數據保存在同一個文檔結構中
-
適用場景:一對一或一對少關系,高頻查詢的子數據。
-
示例:用戶檔案中直接嵌套地址信息
{"user_id": 1001,"profile": {"birthdate": "1990-05-15","education": "碩士"}
}
-
優點:單次查詢獲取全部數據,讀寫高效。
-
缺點:文檔大小可能膨脹(上限 16MB)。
2> 引用:通過存儲數據引用信息來實現兩個不同文檔之間的關聯
-
適用場景:一對多或多對多關系,子數據獨立更新頻繁。
-
示例:用戶與訂單通過?
_id
?關聯
// users 集合
{ "_id": ObjectId("662a1b87c1b6e32a50f1a9e2"), "name": "張三","email": "zhangsan@example.com"
}// orders 集合
{ "_id": ObjectId("55f14312c7447c3da7051b27"),"user_id": ObjectId("662a1b87c1b6e32a50f1a9e2"), // 引用用戶ID"items": ["手機", "耳機"],"total": 5999
}
// 關聯查詢:獲取所有訂單及其對應的用戶信息
db.orders.aggregate([{$lookup: {from: "users", // 關聯的集合名localField: "user_id", // 當前集合的關聯字段foreignField: "_id", // 目標集合的關聯字段as: "user_info" // 輸出字段名(數組)}}
])// 輸出結果
{"_id": ObjectId("55f14312c7447c3da7051b27"),"user_id": ObjectId("662a1b87c1b6e32a50f1a9e2"),"items": ["手機", "耳機"],"total": 5999,"user_info": [ // 注意:結果總是數組{"_id": ObjectId("662a1b87c1b6e32a50f1a9e2"),"name": "張三","email": "zhangsan@example.com"}]
}
-
優點:數據冗余少,易于維護一致性。
-
缺點:需多次查詢(
$lookup
?聚合操作可緩解)。
2.3 存儲引擎:WiredTiger?
存儲引擎負責管理如何在磁盤上存儲數據、處理讀寫操作、實現事務、管理內存緩存等。
MongoDB支持的存儲引擎有MMAPv1,WiredTiger和InMemory。InMemory存儲引擎用于將數據只存儲在內存中,只將少量的元數據(meta-data)和診斷日志(Diagnostic)存儲到硬盤文件中,由于不需要Disk的IO操作,就能獲取所需的數據,InMemory存儲引擎大幅度降低了數據查詢的延遲(Latency)。從mongodb3.2開始默認的存儲引擎是WiredTiger,3.2版本之前的默認存儲引擎是MMAPv1,mongodb4.x版本不再支持MMAPv1存儲引擎。
核心原理:
-
文檔級并發控制:?這是 WiredTiger 高性能的關鍵。它允許對集合中的不同文檔進行并發讀寫操作(寫操作在文檔級別加鎖),極大地提高了多核CPU環境下的吞吐量。相比之前的 MMAPv1 引擎(集合級鎖),這是質的飛躍。
-
寫操作流程
-
數據寫入?Journal 預寫日志(防崩潰)
-
更新內存中的?Cache
-
后臺線程異步刷盤:
-
每 60 秒生成?Checkpoint
-
壓縮后寫入數據文件
-
-
-
寫優化與持久性:
-
Write Ahead Logging (WAL):?所有寫操作首先被順序、快速地寫入一個持久化的?Journal?文件(并默認寫入Cache)。這確保了即使發生崩潰,也能根據 Journal 恢復未刷盤的數據,保證操作的持久性(Durability)。
-
內存管理:?使用緩存(Cache)?來存儲頻繁訪問的數據和索引(通過 LRU 算法管理)。寫操作先在內存的 Cache 中進行修改,然后異步寫入 Journal 和最終刷入數據文件。讀操作優先從 Cache 讀取。
-
Checkpoints:?WiredTiger 定期(默認 60 秒或 Journal 達到 2GB)將內存中修改過的數據(臟頁)以一致的狀態快照寫入數據文件。Checkpoint 是數據的持久化點,縮短了崩潰恢復時需要從 Journal 重放的操作量。
-
-
壓縮:?WiredTiger 支持對數據(Snappy 或 Zlib)和索引(Prefix)進行壓縮,顯著節省磁盤空間并減少 I/O。
-
B-Tree 索引存儲:?WiredTiger 使用 B-Tree 數據結構存儲索引(這是 MongoDB 索引的主要實現方式)。
-
WiredTiger?關鍵配置?(
mongod.conf
)
storage:engine: wiredTigerwiredTiger:engineConfig:cacheSizeGB: 10 # 內存緩存大小 (建議為物理內存50%)journalCompressor: snappy # 日志壓縮算法collectionConfig:blockCompressor: zstd # 數據壓縮算法 (zstd平衡性能/壓縮比)indexConfig:prefixCompression: true # 索引前綴壓縮
三、事務
-
支持范圍:?MongoDB 4.0+ 支持多文檔事務(ACID),適用于副本集;MongoDB 4.2+ 支持分布式事務(跨分片事務)。
-
實現原理:
-
使用快照隔離 (Snapshot Isolation)?級別。事務看到的是事務開始時數據庫的一致性快照。
-
在 WiredTiger 存儲引擎中,事務通過 MVCC (多版本并發控制) 實現。寫操作在事務提交前對其他事務不可見。
-
涉及跨分片事務時,由協調者(通常是發起事務的 Mongos 或 Mongod)使用?Two-Phase Commit (2PC)?協議協調所有參與分片。
-
-
注意事項:?雖然提供了 ACID 保證,但分布式事務比單文檔操作開銷大得多,應謹慎使用,避免長時間運行的事務。單文檔操作天生具有原子性。
舉例:
const session = db.getMongo().startSession(); // 1. 創建會話
session.startTransaction({ // 2. 啟動事務readConcern: { level: "snapshot" },writeConcern: { w: "majority" }
});
try {const coll = session.getDatabase("test").users;coll.updateOne({ name: "Alice" }, { $inc: { balance: -100 } }); // 3. 操作1coll.updateOne({ name: "Bob" }, { $inc: { balance: 100 } }); // 4. 操作2session.commitTransaction(); // 5. 提交事務
} catch (error) {session.abortTransaction(); // 6. 出錯回滾
}