1. 程式人生 > >MongoDB應用案例:使用 MongoDB 儲存商品分類資訊

MongoDB應用案例:使用 MongoDB 儲存商品分類資訊

針對不同商品,建立不同的表

比如音樂專輯、電影這2種商品,有一部分共同的屬性,但也有很多自身特有的屬性,可以建立2個不同的表,擁有不同的schema。

CREATE TABLE `product_audio_album` (
    `sku` char(8) NOT NULL,
    ...
    `artist` varchar(255) DEFAULT NULL,
    `genre_0` varchar(255) DEFAULT NULL,
    `genre_1` varchar(255) DEFAULT NULL,
    ...,
    PRIMARY KEY(`sku`))
...
CREATE TABLE `product_film` (
    `sku` char(8) NOT NULL,
    ...
    `title` varchar(255) DEFAULT NULL,
    `rating` char(8) DEFAULT NULL,
    ...,
    PRIMARY KEY(`sku`))
...

這種做法的主要問題在於

  • 針對每個新的商品分類,都需要建立新的表
  • 應用程式開發者必須顯式的將請求分發到對應的表上來查詢,一次查詢多種商品實現起來比較麻煩

所有商品儲存到單張表

CREATE TABLE `product` (
    `sku` char(8) NOT NULL,
    ...
    `artist` varchar(255) DEFAULT NULL,
    `genre_0` varchar(255) DEFAULT NULL,
    `genre_1` varchar(255) DEFAULT NULL,
    ...
    `title` varchar(255) DEFAULT NULL,
    `rating` char(8) DEFAULT NULL,
    ...,
    PRIMARY KEY(`sku`))

將所有的商品儲存到一張表,這張表包含所有商品需要的屬性,不同的商品根據需要設定不同的屬性,這種方法使得商品查詢比較簡單,並且允許一個查詢跨多種商品,但缺點是浪費的空間比較多。

提取公共屬性,多表繼承

CREATE TABLE `product` (
    `sku` char(8) NOT NULL,
    `title` varchar(255) DEFAULT NULL,
    `description` varchar(255) DEFAULT NULL,
    `price`, ...
    PRIMARY KEY(`sku`))

CREATE TABLE `product_audio_album` (
    `sku` char(8) NOT NULL,
    ...
    `artist` varchar(255) DEFAULT NULL,
    `genre_0` varchar(255) DEFAULT NULL,
    `genre_1` varchar(255) DEFAULT NULL,
    ...,
    PRIMARY KEY(`sku`),
    FOREIGN KEY(`sku`) REFERENCES `product`(`sku`))
...
CREATE TABLE `product_film` (
    `sku` char(8) NOT NULL,
    ...
    `title` varchar(255) DEFAULT NULL,
    `rating` char(8) DEFAULT NULL,
    ...,
    PRIMARY KEY(`sku`),
    FOREIGN KEY(`sku`) REFERENCES `product`(`sku`))
...

上述方案將所有商品公共的屬性提取出來,將公共屬性儲存到一張表裡,每種商品根據自身的需要建立新的表,新表裡只儲存該商品特有的資訊。

Entity Attribute Values 形式儲存

所有的資料按照 的3元組的形式儲存,這個方案實際上是把關係型資料庫當KV儲存使用,模型簡單,但應對複雜的查詢不是很方便。

ENTITY ATTRIBUTE VALUES
sku_00e8da9b type Audio Album
sku_00e8da9b title A Love Supreme
sku_00e8da9b
sku_00e8da9b artist John Coltrane
sku_00e8da9b genre Jazz
sku_00e8da9b genre General

MongoDB 解決方案

MognoDB 與關係型資料庫不同,其無schema,文件內容可以非常靈活的定製,能很好的使用上述商品分類儲存的需求; 將商品資訊儲存在一個集合裡,集合裡不同的商品可以自定義文件內容。

比如一個音樂專輯可以類似如下的文件結構

{
  sku: "00e8da9b",
  type: "Audio Album",
  title: "A Love Supreme",
  description: "by John Coltrane",
  asin: "B0000A118M",

  shipping: {
    weight: 6,
    dimensions: {
      width: 10,
      height: 10,
      depth: 1
    },
  },

  pricing: {
    list: 1200,
    retail: 1100,
    savings: 100,
    pct_savings: 8
  },

  details: {
    title: "A Love Supreme [Original Recording Reissued]",
    artist: "John Coltrane",
    genre: [ "Jazz", "General" ],
        ...
    tracks: [
      "A Love Supreme Part I: Acknowledgement",
      "A Love Supreme Part II - Resolution",
      "A Love Supreme, Part III: Pursuance",
      "A Love Supreme, Part IV-Psalm"
    ],
  },
}

而一部電影則可以儲存為

{
  sku: "00e8da9d",
  type: "Film",
  ...,
  asin: "B000P0J0AQ",

  shipping: { ... },

  pricing: { ... },

  details: {
    title: "The Matrix",
    director: [ "Andy Wachowski", "Larry Wachowski" ],
    writer: [ "Andy Wachowski", "Larry Wachowski" ],
    ...,
    aspect_ratio: "1.66:1"
  },
}

所有商品都擁有一些共同的基本資訊,特定的商品可以根據需要擴充套件獨有的內容,非常方便; 基於上述模型,MongoDB 也能很好的服務各類查詢。

查詢某個演員參演的所有電影,並按髮型日誌排序

db.products.find({'type': 'Film', 'details.actor': 'Keanu Reeves'}).sort({'details.issue_date', -1})

上述查詢也可以通過建立索引來加速

db.products.createIndex({ type: 1, 'details.actor': 1, 'details.issue_date': -1 })

查詢標題裡包含特定資訊的所有電影

db.products.find({
    'type': 'Film',
    'title': {'$regex': '.*hacker.*', '$options':'i'}}).sort({'details.issue_date', -1})

可建立如下索引來加速查詢

db.products.createIndex({ type: 1, details.issue_date: -1, title: 1 })

擴充套件

當單個節點無法滿足海量商品資訊儲存的需求時,就需要使用MongoDB sharding來擴充套件,假定大量的查詢都是都會基於商品型別,那麼就可以使用商品型別欄位來進行分片。

db.shardCollection('products', { key: {type: 1} })

分片時,儘量使用複合的索引欄位,這樣能滿足更多的查詢需求,比如基於商品型別之後,還會經常根據商品的風格標籤來查詢,則可以把商品的標籤欄位作為第二分片key。

db.shardCollection('products', { key: {type: 1, 'details.genre': 1} })

如果某種型別的商品,擁有相同標籤的特別多,則會出現jumbo chunk的問題,導致無法遷移,可以進一步的優化分片key,以避免這種情況。

db.shardCollection('products', { key: {type: 1, 'details.genre': 1, sku: 1} })

加入第3分片key之後,即使型別、風格標籤都相同,但其sku資訊肯定不同,就肯定不會出現超大的chunk。