【Mongo】MongoDB乾貨系列-Variety與Document Validation規範欄位
原文地址:
寫在之前的話
作為近年最為火熱的文件型資料庫,MongoDB受到了越來越多人的關注,但是由於國內的MongoDB相關技術分享屈指可數,不少朋友向我抱怨無從下手。
《MongoDB乾貨系列》將從實際應用的角度來進行MongoDB的一些列乾貨的分享,將覆蓋調優,troubleshooting等方面,希望能對大家帶來幫助。
如果希望瞭解更多MongoDB基礎的資訊,還請大家Google下。
我們知道MongoDB是一個文件型資料庫,scheme free 是其非常重要的特性,但是在生產中我們應該怎麼如合理利用這個特性,合理處理MongoDB的schema呢?
正文
大家都知道MongoDB是文件型資料庫,是Schema Free的。
那麼MongoDB的文件模型能給我們帶來哪些好處呢,在這簡單列舉幾個:
- json形式-在MongoDB中,開發人員可以直接將一個json資料儲存進MongoDB,這對於開發人員來說是非常友好額;
- 讀寫效能高-在關係型資料庫中,我們經常會進行join、子查詢等關聯性需求,這時候往往會帶來較多的隨機IO,而在MongoDB中,我們可以透過合理的資料模型設計來將很多的關聯需求透過內嵌、反正規化的方式實現,減少了隨機IO;
- schema free-MongoDB的資料模型是靈活的,無需為了Online DDL而操心,不同的document也可以有不同的結構。
在這,我們不深入探究如何對於MongoDB 的Schema進行設計、建模,有關這部分內容,推薦大家可以閱讀TJ在開源中國的年終盛典會上分享《MongoDB 進階模式設計》,以及《Retail Reference Architecture Part 1 to 4 》。
在此我們將主要針對進行了初步建模、並正式上線服務後的schema進行巡檢與檢測的方式來進行討論。
Variety
Variety是一個開源的,非常使用的,檢測mongodb表欄位型別、分佈的一個開源工具。
正如其github readme中第一句所說”Meet Variety, a Schema Analyzer for MongoDB”
Variety能夠幫助我們檢測我們MongoDB表中的欄位型別、分佈,並生產報表,可以讓我們非常直觀的對現有表結構、欄位型別進行分析,並找出資料模型中的隱患。
下面我們透過例子來進行講解:
首先,建立一個表
db.users.insert({name: "Tom", bio: "A nice guy.", pets: ["monkey", "fish"], someWeirdLegacyKey: "I like Ike!"}); db.users.insert({name: "Dick", bio: "I swordfight.", birthday: new Date("1974/03/14")}); db.users.insert({name: "Harry", pets: "egret", birthday: new Date("1984/03/14")}); db.users.insert({name: "Geneviève", bio: "?a va?"}); db.users.insert({name: "Jim", someBinData: new BinData(2,"1234")});
我們來看看透過variety獲得的結果
$ mongo test --eval "var collection = 'users'" variety.js +------------------------------------------------------------------+ | key | types | occurrences | percents | | ------------------ | ------------ | ----------- | -------- | | _id | ObjectId | 5 | 100.0 | | name | String | 5 | 100.0 | | bio | String | 3 | 60.0 | | birthday | String | 2 | 40.0 | | pets | Array(4),String(1) | 5 | 40.0 | | someBinData | BinData-old | 1 | 20.0 | | someWeirdLegacyKey | String | 1 | 20.0 | +------------------------------------------------------------------+
test是我們的db名,users是表名。我們可以看到,針對我們之前插入的5條資料,variety跑出的結果是:
所有的document都含有_id,和name欄位,60%的document含有bio欄位,40%的document含有birthday和pets欄位,且pets欄位有2個型別的資料(4個array的,1個string的),20%的document含有someBinData和SomeWeirdLegacyKey欄位。
然而生產環境中由於我們的資料量較大,比如一個表有10億條資料,全部進行掃描會耗時較長,可能我們僅希望對1000條資料進行分析,這時候就可以使用limit來限定。
$ mongo test --eval "var collection = 'users', limit = 1000" variety.js +----------------------------------------------------+ | key | types | occurrences | percents | | ----------- | ----------- | ----------- | -------- | | _id | ObjectId | 1000 | 100.0 | | name | String | 1000 | 100.0 | | someBinData | BinData-old | 1000 | 100.0 | +----------------------------------------------------+
由於MongoDB的可以透過內嵌來減少聯合查詢的需求,可以透過反正規化來減少隨機IO,所以很可能會有巢狀出現在我們的document中。有的時候巢狀的層數太多了,影響我們的統計資訊,怎麼辦,我們可以透過maxDepth來限制。請參考下面的例子:
db.users.insert({name:"Walter", someNestedObject:{a:{b:{c:{d:{e:1}}}}}}); $ mongo test --eval "var collection = 'users'" variety.js +----------------------------------------------------------------+ | key | types | occurrences | percents | | -------------------------- | -------- | ----------- | -------- | | _id | ObjectId | 1 | 100.0 | | name | String | 1 | 100.0 | | someNestedObject | Object | 1 | 100.0 | | someNestedObject.a | Object | 1 | 100.0 | | someNestedObject.a.b | Object | 1 | 100.0 | | someNestedObject.a.b.c | Object | 1 | 100.0 | | someNestedObject.a.b.c.d | Object | 1 | 100.0 | | someNestedObject.a.b.c.d.e | Number | 1 | 100.0 | +----------------------------------------------------------------+ $ mongo test --eval "var collection = 'users', maxDepth = 3" variety.js +----------------------------------------------------------+ | key | types | occurrences | percents | | -------------------- | -------- | ----------- | -------- | | _id | ObjectId | 1 | 100.0 | | name | String | 1 | 100.0 | | someNestedObject | Object | 1 | 100.0 | | someNestedObject.a | Object | 1 | 100.0 | | someNestedObject.a.b | Object | 1 | 100.0 | +----------------------------------------------------------+
又或者我們希望指定統計的條件,比如希望caredAbout為true的,可以這樣做:
$ mongo test --eval "var collection = 'users', query = {'caredAbout':true}" variety.js
又或者是希望進行排序:
$ mongo test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js
同時我們也可以指定分析結果的format:
$ mongo test --quiet --eval "var collection = 'users', outputFormat='json'" variety.js
一般在生產中, 我們不會在primary上進行分析, 我們可以在一個priority為0,且為hidden的secondary上進行分析,這時候需要指定slaveOK:
$ mongo secondary.replicaset.member:31337/somedb --eval "var collection = 'users', slaveOk = true" variety.js
又或者說我們希望將分析結果存在mongo中:
$ mongo test --quiet --eval "var collection = 'users', persistResults=true" variety.js
並且指定儲存的詳細資訊:
- resultsDatabase 分析結果所儲存的db名
- resultsCollection 分析結果所儲存的collection名
- resultsUser 分析結果儲存的例項的user
- resultsPass 分析結果所儲存的例項的password
mongo test --quiet --eval "var collection = 'users', persistResults=true, resultsDatabase='db.example.com/variety' variety.js
我們為什麼要用Variety呢?
儘管我們MongoDB是Schema Free的,但是絕大多數情況下, 我們都希望欄位型別統一。
不一致的欄位型別可能會為我們的資料帶來誤差,試想一下,如果某個欄位的欄位型別不統一,而我們卻不知情,這時候很可能會發現業務查詢有資料丟失,資料不準確。
並且在生產環境中,應用的版本在不斷迭代,需求不斷增多,欄位也隨之變化,如果在沒有規範化的上線流程檢查過後,資料庫中可能還會存在部分資料的欄位確實,比如有的document有a欄位,有的卻沒有,variety也可以幫助我們發現這些問題。
Document Validation
MongoDB 3.2推出了很多給力的功能,在這不得不提及Document Validation,Document Validation的出現我想也是MongoDB官方想表達”schema free but you may need some rules”吧,哈哈,純屬臆測。
簡單介紹下Document Validation:
我們可以為我們schema free的mongodb collection做一些限制。當然這並不是意味著MongoDB變成了關係型資料庫,個人覺得這反而更好的突出了MongoDB Schema free的特性。在正確的地方、需要的地方schema free,在適當的地方要有限制。
假設我們要新建一個表contacts,要有如下約束:
phone欄位為string型別或者email欄位要匹配”@mongodb.com”結尾,或者status為”Unknown”或者”Incomplete”
db.createCollection( "contacts", { validator: { $or: [ { phone: { $type: "string" } }, { email: { $regex: /@mongodb.com$/ } }, { status: { $in: [ "Unknown", "Incomplete" ] } } ] } } )
對已經建立了的表,我們可以透過如下方式來做限定:
db.runCommand( { collMod: "contacts", validator: { $or: [ { phone: { $type: "string" } }, { email: { $regex: /@mongodb.com$/ } }, { status: { $in: [ "Unknown", "Incomplete" ] } } ] }, validationLevel: "moderate" } )
這裡可以看到,多了一個validationLevel引數,我們可以在設定validation的時候指定我們的validationLevel級別:
-
預設級別是strict,對該collection已有的和以後新增的document都進行validation驗證;
-
可以設定為moderate,僅對已經存在的document進行validation限定;
同時還有validationAction引數來指定當有不符合validation規則的資料進行update或者insert的時候, 我們mongodb例項如何進行處理。
- 預設級別為error,mongodb將拒絕這些不符合validation規則的insert和update。
-
可以設定為warn,mongodb會在日誌中記錄,但是允許這類insert和update操作。日誌中如:
2015-10-15T11:20:44.260-0400 W STORAGE [conn3] Document would fail validation collection: example.contacts doc: { _id: ObjectId('561fc44c067a5d85b96274e4'), name: "Amanda", status: "Updated" }
validation的限制
- validation不能對admin、local和config庫中的collection進行設定;
-
不能對system.*這類collections進行validation設定;
-
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29096438/viewspace-2156469/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【Mongo】mongo更新欄位為另一欄位的值Go
- 【mongo】mongo 欄位型別互轉Go型別
- 技術乾貨| MongoDB如何查詢Null或不存在的欄位?MongoDBNull
- 資料庫表欄位命名規範資料庫
- 【乾貨】MySQL資料庫開發規範MySql資料庫
- MongoDB乾貨系列2-MongoDB執行計劃分析詳解(3)MongoDB
- MongoDB乾貨系列2-MongoDB執行計劃分析詳解(2)MongoDB
- MongoDB乾貨系列2-MongoDB執行計劃分析詳解(1)MongoDB
- PHP 擴充套件 Mongo 與 MongoDBPHP套件MongoDB
- api 介面欄位規範的問題,請教大家API
- MYSQL資料庫欄位命名及設計規範MySql資料庫
- 技術乾貨:Applewatch APP設計規範APP
- mongodb如何改_id欄位?MongoDB
- MongoDB更改欄位型別MongoDB型別
- 編碼規範系列:css規範CSS
- 乾貨:圖解演算法——動態規劃系列圖解演算法動態規劃
- mongodb 取欄位最大值MongoDB
- 《Effective Objective-C》乾貨三部曲(二):規範篇Object
- MySQL欄位的取值範圍MySql
- spring data mongo比較兩個欄位查詢SpringGo
- 技術乾貨| MongoDB時間序列集合MongoDB
- 乾貨分享!懸浮按鈕設計規範和經典實踐
- MongoDB中的欄位型別IdMongoDB型別
- 前端規範與思考(二)———css規範前端CSS
- MongoDB Oplog中的欄位介紹MongoDB
- MongoDB資料庫的設計規範MongoDB資料庫
- 規範的位操作方法
- 乾貨系列——模板之圖論1圖論
- AMD規範與CMD規範的區別
- MongoDB(5)- Document 文件相關MongoDB
- mysql和mongodb替換欄位中某字元MySqlMongoDB字元
- MongoDB(13)- 查詢操作返回指定的欄位MongoDB
- MongoDB之資料查詢(欄位判斷)MongoDB
- AI客服上線 乾貨 乾貨 全是乾貨!AI
- JNI開發系列③C語言呼叫Java欄位與方法C語言Java
- Redis系列24:Redis使用規範Redis
- 一個篩選mongo存在某個欄位的資料的技巧Go
- 一次mongo查詢不存在欄位引發的事故Go