電商業務一個基本的功能模組就是存儲品類豐富的商品信息,各種商品特性、參數各異,MongoDB 靈活的文檔模型非常適合於這類業務,本文主要介紹如何使用 MongoDB 來存儲商品分類信息。
關係型資料庫解決方案
上述問題使用傳統的關係型資料庫也可以解決,例如以下幾種方案
針對不同商品,創建不同的表
這例如音樂專輯、電影電影商品,有一部分共同的屬性,但也有很多自身特有的屬性,可以創建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 解決方案
Mognono
MongoDB 解決方案Mognonoon
MongoDB 解決方案🎜🎜🎜Mognonoon🎜MongoDB 解決方案🎜🎜🎜Mognonoon 對產品的關係,集合裡不同的商品可以自訂文件內容。 🎜🎜例如一個音樂專輯可以類似如下的文檔結構🎜{ 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。