MongoDB實戰-面向文檔的資料(找到最合適的資料建模方式)

來源:互聯網
上載者:User

標籤:schema   mongodb   電子商務   

  前一段時間一直研究通過Ruby操作MongoDB資料庫,在學習的過程中也分享了自己學習成長的過程,撰寫了包含兩篇入門操作文章和十二篇進階文章。本篇文章開始,我們將進入MongoDB的實戰操作流程,MongoDB這一非關係型資料庫-是一個文檔型資料庫,儲存的是面向文檔的資料。

  1. 如何在MongoDB資料庫中使用schema

  設計資料庫schema是在已知資料庫系統特性、資料本質以及應用程式需求的情況下為資料集選擇最佳表述的過程。傳統的關係型資料庫RDBMS中鼓勵使用正規化的資料模型,從而確保資料的可查詢性和解決資料更新帶來的不一致問題。但是schema的設計不是一門精確的科學。當出現要應用程式處理非結構化資料,或者應用程式對效能要求很高時,就可能會要求一個通用的資料模型。MongoDB中缺乏硬性Schema設計規則。

  為了能夠參考傳統RDBMS的schema設計規則,我們首先需要清楚RDBMS和MongoDB在如下三個方面的對應關係和相應區別:

  • 資料的基本單元分別是什嗎?

    在RDBMS中,資料的基本單元指的是帶有列和行的資料表;

    在KVStore for Redis中指向不定類型值的鍵;

    在MongoDB中,資料的基本類型是BSON文檔

  • 如何查詢和更新資料?

    資料查詢操作中:

     RDBMS支援即時查詢和連接操作查詢;

     MongoDB支援即時查詢,但是不支援連接操作;

     簡單的KVStore for Redis只能根據單個鍵來擷取值

    資料更新操作中:

     RDBMS中,可以使用SQL以複雜的方式來更新文檔,將多條更新封裝在一個事務中可以獲得原子性,還可以復原;

     MongoDB不支援事務,但支援多種原子操作,這些操作可以作用於複雜文檔的內部結構;

     簡單的KVStore for Redis中,可以更新一個值,通常每次更新都是將值完全替換掉。

  • 應用程式的訪問模式是什嗎?

    要想確定理想的資料模型,必須問無數個與應用程式有關的問題。讀寫比?需要何種查詢?資料如何更新?並發問題?資料機構化程度?

  總的來說,最好的schema設計總是源於對正在使用的資料庫的深入理解,對應用程式需求的準確判斷以及過去的經驗。

2. 實戰-設計電子商務資料模型

  在本部分,我們將示範如何在MongoDB中對電子商務資料進行建模,我們會關注產品與分類、使用者與訂單、產品評論。對很多開發人員來講,資料建模總會伴隨著對象映射。使用對象映射器有利於進行驗證、類型檢查和關聯。MongoDB沒有對象映射的需要,一方面是因為文檔已經是類似對象的表述了,同時驅動程式為MongoDB提供了相當高階的介面,參考前序博文的學習,使用驅動介面就能在MongoDB上構建完整的應用程式。很多成熟的MongoDB對象映射器在基礎語言驅動之上又提供了一層額外的抽象。

  由於最終還是需要跟文檔打交道,關注文檔本身,認識到一個精心設計的MongoDB Schema裡文檔是什麼樣的,能讓我們更好的使用該資料庫。

2.1 產品與分類

  產品和分類是會出現在任何電子商務網站的資訊。在傳統的RDBMS中,產品會使用大量的資料表,比如儲存基本資料的表,儲存關聯送貨資訊和價格曆史的表,以及其他可能會出現的一系列複雜屬性。這種多表schema在RDBMS的表連接能力的協助下很有用。

  但是在MongoDB資料庫中,對產品進行建模會相對簡單。集合并不一定有schema。任何產品資訊文檔都可以容納產品所需的各種動態屬性。通過使用數組來容納內部文檔結構,還可以將RDBMS裡的多表描述為一個MongoDB集合。

 下面是一個取自園藝商店的樣本產品

doc={   _id:new ObjectId("59884b76b53fab2a8024b6ad"),   slug:"wheel-barrow-9092",   sku:"9092",   name:"Extra Large Wheel Barrow",   description:"Heavy duty wheel barrow",   details:{       weight:47,       weight_unite:"1bs",       model_num:40392882,       manufacturer:"Acme",       color:"Green"   },   total_review:4,   average_review:4.5,   pricing:   {     retail:589700,     sale:489700   },   price_history:[   {   retail:529700,   sale:429700,   start:new Date(2010,4,1),   end:new Date(2010,4,8)   },   {   retail:529700,   sale:529700,   start:new Date(2010,4,9),   end:new Date(2010,4,16)   }   ],   cateory_ids:[    new ObjectId("59884ee3b53fab2a8024b6ae"),    new ObjectId("59884ee3b53fab2a8024b6af")   ],   main_cate_id:new ObjectId("59884ee3b53fab2a8024b6b1"),   tags:["tools","gardening","soil"]}

  在此,如果要為文檔產生一個URL,通常建議設定一個簡短名稱欄位。且該欄位應該有唯一索引,這樣就可以把其中的值用作主鍵。假設將此文檔儲存在products集合裡,可以像下面一樣建立唯一性索引。

db.products.ensureIndex({slug:1},{unique:true})

  由於在slug上存在唯一索引,插入文檔時需要使用安全模式,這樣就可以知道插入成功與否。在Ruby中執行插入代碼

@products.insert({:name=>"Extra Large Wheel Barrow",                 :sku=>"9092",                 :slug=>"wheel-barrow-9092"},                 :safe=>true                 )

  代碼中的:safe=>true;如果插入成功,就不會拋出異常,表明選擇了一個唯一的簡短名稱;如果拋出異常,代碼需要使用一個新的簡短名稱進行重試。上述文檔中後續儲存了details-不同產品的詳細資料,接著儲存了當前價格pricing和曆史價格price_history,category_ids儲存了標籤名稱的數組。

  RDBMS資料庫可以使用join操作進行多表聯集查詢。作為不支援連接查詢的MongoDB資料庫,如何支援多對多的策略呢?文檔中儲存category_ids數組,其中包含的是一個對象ID的數組,每個對象ID都是一個指標,指向某個分類文檔的_id欄位。下面是一個分類文檔的示範:

doc={     _id:new ObjectId("59884ee3b53fab2a8024b6ae"),     slug:"gradening-tools",     ancestors:[     {        name:"Home",        _id:new ObjectId("59884ee3b53fab2a80240003"),        slug:"home"     },     {        name:"Outdoors",        _id:new ObjectId("59884ee3b53fab2a80240001"),        slug:"outdoors"     }     ],     parent_id:new ObjectId("59884ee3b53fab2a80240001"),     name:"Gardening Tools",     description:"Gardening gadgets galore"}

  觀察產品文檔的category_ids欄位裡的對象ID,發現該產品關聯了Gardening Tools分類。在產品文檔中放入category_ids的數組鍵讓那些多對多的查詢成為可能。

  查詢Gardening Tools分類裡的所有產品

db.products.find({category_ids=>category{‘_id‘}})

  查詢指定產品的所有分類,可以使用$in操作符,它類似於SQL的IN指令。

db.categories.find({_id:{$in:procuct[‘category_ids‘]}})

  分類文檔中,存放父文檔數組的含義是去正規化,將上級分類的名稱放入每個子分類的文檔裡,這也是由於MongoDB不支援關聯查詢。這樣一來,查詢Gardening Tools分類時,就不需要執行額外的查詢來擷取上級分類(Outdoors和Home)的名稱和URL了。

2.2 使用者與訂單

  看看如何對使用者和訂單建模,以此闡明另一種常見關係——一對多關聯性。一個使用者可能會擁有多張訂單。在RDBMS中,會在訂單表中使用外鍵;在MongoDB中慣例很相似,如:

doc={  _id:new ObjectId("6a5b1476238d3b4dd5000001"),  user_id:new ObjectId("4a5b1476238d3b4dd5000001"),  state:"CART",  line_items:[{      _id:new ObjectId("4a5b1472134d3b4dd5000921"),      sku:"9092",      name:"Extra Large Wheel Barrow",      quantity:1,      pricing:{          retail:5897,          sale:4897,           }  },  {      _id:new ObjectId("4a5b1472134d3b4dd5000922"),      sku:"10027",      name:"Rubberized Work Glove,Block",      quantity:2,      pricing:{          retail:1499,          sale:1299,           }  }  ],  shipping_address:{    street:"588 5th Street",    city:"Brooklyn",    state:"NY",    zip:11215   },  sub_total:6196}

  訂單中的第二個屬性user_id儲存了一個使用者的_id,它是指指向樣本使用者的指標。這樣的設計可以方便地查詢關係中的任意一方。要尋找一個使用者的所有訂單:

db.orders.find({user_id:user{‘_id‘}})

要擷取指定訂單的使用者同樣簡單:

user_id=order[‘user_id‘]db.users.find({_id:user_id})

上面的訂單表述方式有明顯的優點,首先,它易於理解,完整的訂單概念都能被封裝在一個實體裡,包括條目明細、寄送地址以及最終的支付資訊。查詢資料庫時,可以通過一條簡單的查詢返回整個訂單對象。其次,可以把產品在購買時的資訊儲存在訂單文檔裡,這樣能夠輕易地查詢並修改訂單資訊。

  使用者的文檔也使用了類似的模式。儲存了一個地址文檔的列表和一個支付方式的列表。在文檔的最上層還能找到任何使用者模型裡都有的基本常見屬性。

doc={_id:new ObjectId("4a5b1476238d3b4dd5000001"),email:"[email protected]",first_name:"Kyle",last_name:"Banker",hashed_password:"bd1cfa194c3a603e7186780824b04419",address:[{ name:"home",  street:"588 5th Street",  city:"Brooklyn",  state:"NY",  zip:10010},{  name:"work",  street:"1 E.23rd Street",  city:"New York",  state:"NY",  zip:10010  }],payment_methods:[{  name:"VISA",  last_four:2127,  crypted:"43f6baldfda6b8106dc7",  expiration_date:new Date(2014,4)}]}

2.3 評論資訊

  一般產品都會有評論資訊。一般而言,一個產品有多個評論,該關係是也用對象ID應用product_id來編碼的。

doc={ _id:new ObjectId("4c4b1476238d3b4dd5000041"), product_id:new ObjectId("59884b76b53fab2a8024b6ad"), date:new Date(2010,5,7), title:"Amazing", text:"Has a squeaky wheel,but still a darn good wheel barrow", rating:4, user_id:new ObjectId("4a5b1476238d3b4dd5000001"), user_name:"dgreenthumb", helpful_votes:3, voter_ids:[ {new ObjectId("59884b76b53fab2a8024b600")}, {new ObjectId("59884b76b53fab2a8024b601")}, {new ObjectId("59884b76b53fab2a8024b602")}}

  上面的評估資訊中,由於MongoDB不支援連接查詢,所以冗餘儲存了user_name,同時還有一個voter_ids數組,用於儲存對該評論進行投票的使用者。去除了重複投票,同時也讓我們有能力查詢某個使用者投過票的所有評論。

  至此,我們已經覆蓋了電子商務的資料模型了,講解了具體的建模方法,以及由於MongoDB不支援連接查詢帶來的局限性問題的去正規化解決方案,從而找到一個最適用於應用的schema。

本文出自 “techFuture” 部落格,謝絕轉載!

MongoDB實戰-面向文檔的資料(找到最合適的資料建模方式)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.