1. 程式人生 > 實用技巧 >MongoDB學習5:模型設計和設計模式

MongoDB學習5:模型設計和設計模式

1.資料模型

  1.1 什麼是資料模型?

  資料模型是一組由符號、文字組成的集合,用以準確表達資訊,達到有效交流、溝通的目的

  1.2 資料模型設計元素

  • 實體 Entity
    • 描述業務的主要資料集合
  • 屬性 Attribute
    • 描述實體裡面的單個屬性
  • 關係 Relationship
    • 描述實體與實體之間的資料規則
    • 結構規則:1-N N-1 N-N
    • 引用規則:比如電話號碼不能單獨存在,必須依賴於具體的人

2.JSON文件模型設計特點

  2.1 MongoDB文件模型設計的三個誤區

  • 不需要模型設計
  • MongoDB應該用一個超級大文件來組織所有資料
  • MongoDB不支援關聯或者事務

  2.2 為什麼說MongoDB是無模式的

  • 嚴格來說,MongoDB同樣需要概念/邏輯建模
  • 文件模型設計的物理層結構可以和邏輯層類似
  • 可以省略物理建模的具體過程

  2.3 文件設計模式原則:效能和易用

  2.4 關係模型 vs 文件模型

關係資料庫 JSON文件模型
模型設計層次 概念模型、邏輯模型、物理模型 概念模型、邏輯模型
模型實體 集合
模型屬性 欄位
模型關係 關聯關係,主外來鍵 內嵌陣列、引用欄位

3.MongoDB文件模型設計三部曲

  3.1 建立基礎文件模型

  • 根據概念模型或者業務需求推匯出邏輯模型 -找到物件

  • 列出實體之間的關係 -明確關係

  • 套用邏輯設計原則來決定內嵌方式 -進行建模

  • 完成基礎模型構建

  • 一個聯絡人管理應用的例子

    • 1.找到物件
      -- Contacts
      -- Group
      -- Address
      -- Portraits
    • 2.明確關係
      -- 一個聯絡人有1個頭像 (1-1)
      -- 一個聯絡人可以有多個地址 (1-N)
      -- 一個聯絡人可以屬於多個組,一個組可以有多個聯絡人(N-N)
    • 3.關係建模(1-1):Portraits
      -- 基本原則:一對一關係以內嵌為主,作為子文件形式或者直接在頂級,不涉及到資料冗餘
      -- 例外情況:如果內嵌後導致文件大小超過16MB
    • 4.關係建模(1-N):Address
      -- 基本原則:一對多關係同樣以內嵌為主,用陣列來表示一對多,不涉及到資料冗餘
      -- 例外情況:內嵌後導致文件大小超過16MB、陣列長度太大(數萬或更多)、陣列長度不確定
    • 5.關係建模(N-N):Groups
      -- 基本原則:不需要對映表,一般用內嵌陣列來表示一對多,通過冗餘來實現N-N
      -- 例外情況:內嵌後導致文件大小超過16MB、陣列長度太大(數萬或更多)、陣列長度不確定

  3.2 根據讀寫工況細化

  • 聯絡管理應用的分組需求
    Q : 假如有千萬級聯絡人;需要頻繁變動分組的資訊,比如增加分組及修改名稱及描述以及營銷狀態;一個分組有百萬級聯絡人,如何解決?
    A : Group使用單獨的集合

  • 什麼時候應該使用引用方式?

    • 內嵌文件太大,數MB或者超過16MB
    • 內嵌文件或陣列元素會頻繁修改
    • 內嵌陣列元素會持續增長並且沒有封頂
  • MongoDB引用設計的限制

    • MongoDB對使用引用的集合無主外來鍵檢查(需要程式自行判斷)
    • MongoDB使用聚合框架的 $lookup 來模仿關聯查詢
    • $lookup 只支援 left outer join
    • $lookup 的關聯目標(from)不能是分片表
db.contacts.aggregate([
 {
   $lookup:{
     from:"groups",   #外聯表
     localField:"group_ids",    #外來鍵欄位
     foreignField:"group_id",    #外聯表主鍵
     as:"groups"     #查詢結果
   }
 }
])

  3.3 套用設計模型

  • 物聯網場景下的海量資料處理 - 飛機監控資料
    -- 要求記錄飛機的實時位置,假設有10萬架飛機、1年的資料、每分鐘一條
    -- 如果每架飛機每分鐘都往資料庫寫入一條資料,那麼資料量將非常龐大,如何解決呢?可以使用分桶設計解決:一個文件儲存一架飛機一小時的資料
每分鐘1個文件 每小時1個文件
文件條數 52.6B 876M
索引大小(_id index \ {ts:1,deviceId:1}) 6364GB(1468GB \ 4895GB) 106GB(24.5GB \ 81.6GB)
文件平均大小 92 Bytes 758 Bytes
資料大小 4503GB 618GB
  • 大文件,很多欄位,很多索引
    -- 比如一個表中存在各種各樣的名字(chineseName,englishName,franchName...),而且每個名字會頻繁的查詢。可以使用列轉行,將多個相同的列轉化為一個數組(names:{ chinese:'',english:'',franch:'' }

  • 模型靈活了,如何管理文件的不同版本?
    -- 增加一個版本欄位

  • 統計網頁流量點選
    -- 如果每次點選頁面都產生一個計數更新操作,那麼資料庫將大量由此操作佔據了
    -- 這種統計數字準確性並不十分重要,可以使用近似計算優化:每10次操作計數一次寫入庫數值10

if random(0,9) == 0
  increment by 10
  • 業績排名、遊戲排名、商品銷售統計等精確統計
    -- 如某個商品今天賣了多少、本週賣了多少、本月賣了多少
    -- 傳統解決方案是通過聚合計算,但是消耗資源多,聚合計算時間長;使用聚合欄位
{
  product:"洗衣服",
  sku:"10000",
  price:23.99,
  stock:9999,
  daily_sales:10,
  weekly_sales:100,
  monthly_sales:700
}

db.products.update({_id:123,{
  $inc:{
    stock:-1,
    daily_sales:1,
    weekly_sales:1,
    monthly_sales:1
  }
}})
  • 模式小結
模式 場景 痛點 設計模式的方案及優點
分桶 時序資料(物聯網、智慧城市、智慧交通) 資料點採集頻繁,資料量太多 利用文件內嵌陣列,將一個時間段的資料聚合到一個文件裡
大量減少文件數量
大量減少索引佔用空間
列轉行 產品屬性(color、size...)
多語言(多國家)屬性
文件中有很多類似的欄位
會用於組合查詢搜尋,需要建立很多索引
轉化為陣列,一個索引解決所有查詢問題
版本欄位 任何有版本衍變的資料庫 文件模型格式多,無法知道其合理性
升級時需要更新太多文件
增加一個版本號欄位
快速過濾掉不需要升級的文件
升級時對不同版本的文件做不同的處理
近似計算 網頁計數,各種結果不需要準確的排名 寫入太頻繁,消耗系統資源 間隔寫入,每隔10次或100次寫入
大量減少寫入操作
預聚合 準確排名、排行榜 統計計算耗時,計算時間長 模型中直接增加統計欄位
每次更新資料的同時更新統計值