MongoDB:開發人員實踐指北
1、左右爲難:MySQL vs MongoDB
1.1 對比一覽
1.2 從使用看對比
1.2.1 文檔數據類型:字段結構更豐富
-
MongoDB 的數據類型非常豐富,BSON/JSON 結構,可以表示任意結構的數據。
-
MySQL 的數據類型相對貧瘠,行列結構,層級結構數據需要拆表、聯立表示,複雜了數據管理方式。除此之外,業務數據與 DB 數據需要 ORM 來完成映射表示,代碼複雜度較高。
1.2.2 Schema:讓數據更靈活
-
MongoDB 是動態 Schema,無需預先定義 Collection 結構;而 MySQL 是靜態 Schema,需要預先定義 Table 結構。舉例來說,如果我們的數據時效性以一個月爲限,很少會追溯過期數據,我們更希望將表以時間爲劃分依據,如 Data-201901、Data-201902 等,這在 MongoDB 中可以輕易實現,而如果在 MySQL 中則要複雜許多,要麼動態執行 Create Table 語句,要麼將數據放在一起。在之前寫過的 Slowlog 管理系統中,因爲數據量較大,採用 MySQL 存儲數據時,需要定時任務去定期歸檔數據,代碼複雜度搞。
-
MongoDB 是 NoStrict Schema,同一 Collection 的文檔結構允許不同;而 MySQL 是 Strict Schema,同一 Table 的字段結構必須相同。因此,爲了實現複雜的數據結構,MySQL 常常會將一個業務結構拆成多張表表示,也會通過空字段處理,即有的記錄的字段有值,有的是空值。非強制性 Schema 最大的好處就是支持業務變化、代碼重構,尤其是業務不穩定階段,表(集合)結構經常變化,但又害怕數據丟失,因此只能不斷加字段,導致一張表中會有很多無效字段,而在非強制 Schema 中,每條記錄(文檔)的結構都可以不一樣,因此新變化對老數據無侵害。
1.2.3 事務管理:MongoDB 的短板
事務管理是 MongoDB 的軟肋,目前 v4.2 版本已經推出了事務功能,但事務操作必須針對已存在的集合(Existing Collection)。
2、MongoDB 常見操作
2.1 查看庫表
show dbs // 列舉MongoDB實例的數據庫
show collections // 列舉當前庫下的所有集合
2.2 查看錶數據
MongoDB 的查詢語法比較豐富,既有普通文檔的條件查詢,還有嵌入文檔、數組字段的查詢,具體語法可以參見官網 https://docs.mongodb.com/manual/tutorial/query-documents/,MongoDB 的文檔除了講解說明外,還有交互終端,用戶可以自由使用。
無論是何種查詢,最終都離不開兩個問題:(1)查詢條件是什麼?(2)查詢結果又是什麼樣子的?
2.2.1 查詢表達式(Query Selector)
如上是 MongoDB 的一些操作符,通過這些操作符和字段、值的聯立組合,可以形成複雜的查詢條件。
假設,我們要在部署記錄集合(deploy_record)中查詢用戶(sli4)、在 12 月部署的失敗(Failure)和取消(Cancel)的記錄:
db.deploy_record.find({
user:"sli4",
date: {
"gte": ISODate("2019-12-01T00:00:00Z),
"lt": ISODate("2020-01-01T00:00:00Z)
},
status:{$in: ["Failure", "Cancel"]}
})
很多技術內容,光看文檔是無法真正領會的,需要有真實的操作場景,才能碰到自己想不到的問題。本想寫一篇概括性、總結文章,可以一頁指北,卻發現,只能提供概要性說明,具體實踐過程,還是要面向文檔和 Google 編程!
2.2.2 投射
投射,就是 SQL 中的字段選擇。MongoDB 默認返回符合查詢條件文檔的全部字段,爲了限制 MongoDB 返回的數據量,可以通過投射表達式選擇需要的字段。
db.collection.find({查詢條件},{投射表達式})
投射表達式,就是爲指定字段設定是否返回標識,1 標識返回,0 標識不返回。如部署記錄(deploy_record)只返回應用(app)、狀態(status):
注意:MongoDB 默認是返回主鍵(_id),因此如果不需要主鍵時,需要顯示指定_id:0。
2.2.3 聚合
Aggregation operations process data records and return computed results. 聚合操作通過數據分組、針對分組數據執行一系列操作、最終生成單一計算結果值。聚合提供 Pipeline、MapReduce、Methods 三種方式。聚合操作可以簡化應用層的業務邏輯,在數據層提取需要的數據值。
因爲聚合比較重要,也很複雜,我們將在下面另起一個模塊講解說明。
2.3 創建數據:insert
db.collection.insertOne() // 插入單一文檔
db.collection.insertMany() // 插入多個文檔
db.collection.insert() // 插入一個或多個文檔
MongoDB 的數據插入行爲有三點內容需要明確:
-
如果插入數據時,集合尚未存在,insert 操作會觸發集合創建。
-
mongodb 中每個文檔都是以_id 作爲主鍵,如果插入的文檔中沒有指定_id,mongodb 會默認分配唯一 ID。
-
無論插入一個還是多個文檔,mongodb 都是以單一文檔爲原子性維度,即插入過程中如遇錯誤,隻影響正在插入的文檔,不影響已經成功的文檔,不會回退至插入操作前的狀態。
2.4 修改操作
// 修改操作的命令格式
{
<update operator> : { <field1>:<value1>, ... },
<update operator> : { <field1>:<value1>, ... },
...
}
2.4.1 修改還是替換
上圖的兩個操作,一個是替換,一個是字段更新,在沒有 $set 更新操作符時,是以新值替代舊值;而有 $set 時則只針對指定字段更新。
db.deploy_record.update({_id: 1}, {user: "王五"})
db.deploy_record.update({_id: 1}, {$set: {user: "王五"}})
2.4.2 更新操作注意事項
-
_id 字段不可更改
-
upsert 操作:當 upsert 爲 true 時,如果沒有匹配的文檔可供修改,會將更新文檔作爲新文檔插入集合。
2.4.3 還有更多操作符,具體使用結合文檔和場景
https://docs.mongodb.com/manual/reference/operator/update/
2.5 刪除操作
db.collection.delete({查詢表達式})
3. MongoDB 的高級操作 -- 聚合
3.1 Pipeline
3.2 MapReduce
MapReduce 是大數據處理模型,通過 map 和 reduce 兩個階段獲取理想的數據結果:
-
Map:通過 map 函數提取一組 kv 對,每個 key 都有多個 value,準確的說是 key-values。
-
Reduce:通過 reduce 函數,以 key 爲組,分組計算求得結果。
在 MongoDB 中,MapReduce 的命令格式如下:除了提供 map、reduce 函數外,還需要指定數據的篩選條件(query)和數據結果的保存集合(out)
{
function map(){},
function reduce(key, values){},
{
query: {查詢表達式},
out: 輸出集合
}
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/NXQ7bSb8UEH0VqOwFxOj3g