mongodb 最佳實踐

NoSQL MongoDB 物理 JSON Zach馨月 Zach馨月 2017-11-02

模式設計

不要按照關係型來設計表結構

MongoDB可以讓你像關係型數據庫一樣設計表結構,但是它不支持外鍵,也不支持複雜的Join!如果你的程序發現有大量實用JOIN的地方,那你的設計可能需要重新來過。參照以下相關模式設計建議。

數據庫集合(collection)的數量不宜太多

MongoDB的模式設計基於靈活豐富的JSON文檔模式。在很多情況下,一個MongoDB應用的數據庫內的集合(表)的數量應該遠遠小於使用關係數據庫的同類型應用。MongoDB表設計不遵從第三範式。MongoDB的數據模型非常接近於對象模型,所以基本上就是按照主要的Domain object的數量來建相應的集合。根據經驗,一般小型應用的集合數量通常在幾個之內,中大型的應用會在10多個或者最多幾十個。

不要害怕數據冗餘

MongoDB模式設計不能按照第三範式,很多時候允許數據在多個文檔中重複,比如說,在每一個員工的文檔中重複他的部門名字,就是一個可以接受的做法。如果部門名字改了,可以執行一個update({},{}, {multi:true}) 的多文檔更新來一次性把部門名字更新掉。

適合和不適合冗餘的數據類型

一般來說,如果某個字段的數據值經常會變,則不太適合被大量冗餘到別的文檔或者別的集合裡面去。舉例來說,如果我們是在做一些股票類型資產管理, 可能有很多人都購買了Apple的股票,但是如果把經常變動的股價冗餘到客戶的文檔裡,由於股票價格變動頻繁,會導致有大量的更新操作。從另外一個角度來說,如果是一些不經常變的字段,如客戶的姓名,地址,部門等,則可以儘管進行冗餘shi’yang

對 1:N(一些)的關係使用全部內嵌

對於一對多的關係,如一個人有幾個聯繫方式,一本書有10幾個章節,等等,建議使用內嵌方式,把N的數據以數組形式來描述,如:

 > db.person.findOne() { user_id: 'tjworks', name: 'TJ Tang',  contact : [ { type: 'mobile', number: '1856783691' }, { type: 'wechat', number: 'tjtang826'} ] }

對 1: NN (很多) 的關係使用ID內嵌

有些時候這個一對多的多端數量較大, 比如說,一個部門內有多少員工。在華為一個三級部門可能有數千員工,這個時候如果把所有員工信息直接內嵌到部門內肯定不是個好的選擇,有可能會超出16MB的文檔限制。這個時候可以採用引用ID的方式:

> db.departments.findOne(){ name : 'Enterprise BG', president: 'Zhang San', employees : [ // array of references to Employee colletion ObjectID('AAAA'),  ObjectID('F17C'),  ObjectID('D2AA'), // etc ]}

如果需要查詢部門下員工相關信息,你可以使用$lookup聚合操作符來把員工信息進行關聯並返回。

對 1: NNN (很多) 的關係使用

如果一對多情況下,這個多端數量無限大並會頻繁增長,比如說,一個測量儀的每分鐘讀數,一年下來有幾十萬條,這個時候即使是把ID放到數組裡都會管理不便,這個時候就應該把多端的數據創建一個集合,並在那個集合的文檔里加入對主文檔的連接引用,如:

 > db.sensors.findOne() { _id : ObjectID('AAAB'), name : 'engine temperature', vin : '4GD93039GI239', engine_id: '20394802', manuafacture: 'First Motor', production_date: '2014-02-01' ... } >db.readings.findOne() { time : ISODate("2014-03-28T09:42:41.382Z"), sensor: ObjectID('AAAB'), reading: 67.4  }

把二進制大文件和元數據分集合存放

如果你有需要把PDF文件,圖片,甚至小視頻等二進制文件需要管理,建議使用MongoDB 的GridFS API 或者自己手動分集合來分開管理二進制數據和元數據。

經常更新的數據不要放在嵌套數組內

數組是用來表達 1對多關係的利器,但是MongoDB對嵌套的數組內元素缺乏直接更新能力。比如說:

{ name: "Annice", courses: [ { name: "English", score: 97 }, { name: "Math", score: 89 }, { name: "Physics", score: 95 } ]}

這樣設計沒有嵌套數組,我們可以直接對 Math的score 修改為99:

db.students.update({name: "Annice", "courses.name":"Math"}, {$set:{"courses.$.score": 99 }})

注意數組定位符 $ 的用法,$ 表示當前匹配的第一個數組元素的在數組內的索引。

但是下面這種情況就涉及到了數組嵌套:

 { name: "Annice", courses: [ { name: "Math", scores: [ {term: 1, score: 80} , {term: 2, score: 90} ] }, { name: "Physics", score: 95 } ] }

這個時候如果你想對Math course的term 1的Score進行修改,你就需要把 scores 這個數組整個調到內存然後在代碼裡對這個嵌套數組的元素進行修改。這是因為MongoDB的數組定位符 $ 只對第一層數組有效。

當然,如果你的模型不需要修改嵌套的數組內元素,那麼這條就不適用。

相關推薦

推薦中...