淘寶的電商產品種類非常豐富,必然得力於其商品模型的高度通用性和擴展性。
下面我將親自操作淘寶商品的發布過程,結合網上其他博客對淘寶網商品庫的分析,簡單談談我的理解。
注:下面不特殊說明,各個表除主鍵外的無需建立其他唯一索引。
品類
在淘寶網發布寶貝,需要先選擇所屬的品類:
我要賣的商品,屬於『流行男裝 -> 帆布鞋 -> YINGLUNKUANGWEI/英倫匡威 』這個品類。可見,品類存在層級關系,流行男裝是帆布鞋的父品類,英倫匡威屬於帆布鞋的子品類,同時英倫匡威也是一個品牌。
撇開品牌不談,維護這樣一個品類關系,應該有這樣一個表結構:
品類ID | 主鍵 |
父品類ID | 父級品類ID |
品類名稱 | 例如:帆布鞋 |
品類權重 | 上圖每一列展示有次序先后之分 |
商品
選好品類好,點擊編輯寶貝,最上面是這樣的:
也就是說,你要編輯一個商品的名稱和描述,並且商品屬於一個分類,因此商品表大概如此:
商品ID | 主鍵 |
品類ID | 最子品類,比如:衣服->毛衣,那么這里應該是毛衣品類ID |
標題 | 例如:一個YY出來的匡威鞋 |
描述 | 例如:買不了吃虧,買不了上當 |
屬性和屬性值
寶貝屬性部分,淘寶規定了若干必填項和選填項,其中必填項一般用來商品檢索用途,比如進入檢索頁可以看到"鞋幫高度"和"閉合方式"的篩選項:
我們知道,淘寶售賣各式各樣的商品,需要支持各式各樣的屬性信息,比如:尺寸,顏色...等等。同樣的道理,單單就顏色這個屬性來說,其對應的屬性值也可能是:綠色,紅色...等等。
因此,我們可以用屬性表存儲屬性,用屬性值表存儲與屬性相關聯的可選值,例如下面:
屬性表
屬性ID | 主鍵 |
屬性名稱 | 例如:顏色 |
屬性值表
屬性值ID | 主鍵 |
所屬的屬性ID | 例如:歸屬於 顏色 屬性 |
屬性值 | 例如:紅色 |
有這兩個表之后,可以找一個運營人員小C專門負責接屬性&屬性值的增刪改查需求,比如:
負責鞋品類的運營小A找小C說:顏色下面給我加一個彩虹色。
負責車品類的運營小B找小C說:顏色下面給我加一個藍色。
在小C編輯完成后,小A可以給自己的品類引入:顏色 -> 彩虹色的選項,小B可以給自己的品類引入:顏色->藍色的選項。
上面這段工作場景,其實引申出了接下來要談到的另一個關鍵點,就是品類和屬性&屬性值的關系。
品類和屬性&屬性值
前面我們談到小C的工作職責,是接小A和小B的需求,其中小A負責鞋品類,小B負責車品類。現在小C維護的數據中,顏色屬性有2個值:彩虹色,藍色,但是小A的品類只需要彩虹色,小B的品類只需要藍色,如果他們倆去查詢小C的屬性值庫會得到彩虹色,藍色兩個選項,這將導致一個淘寶賣家在編輯一輛汽車的時候可以選擇彩虹色,可是我還沒見過汽車是彩虹色噴漆的呢!
問題就在於,小C維護的是整個淘寶所有的屬性與屬性值,而小A和小B需要的僅僅所有屬性中的部分,以及屬性對應的屬性值中的部分,因此我們需要維護一個"品類"與"屬性","屬性值"之間的關系,從而形成一個子集。
我們先建一個"品類屬性"關系表,來標明某個品類關聯到哪些屬性子集合:
品類屬性模板ID | 主鍵 | |
品類ID | 例如:帆布鞋的 品類ID | |
屬性ID | 例如:顏色的 屬性ID | |
是否必填 | 之前的圖片里,帶*號的屬性 |
再來建一個"品類屬性值"關系表,來標明某個品類某個屬性關聯到哪些屬性值子集合:
品類屬性模板ID | 例如:上面那個表的主鍵 |
屬性值ID | 例如:藍色 的屬性值ID |
經過這樣的設計,我通過先選擇品類,就可以通過品類ID查詢上述2個表得到屬性子集合和屬性值子集合,也就是最上面看到的各種"寶貝屬性"的數據來源了。
PS:品類屬性模板表沒有對"品類ID+屬性ID"做唯一性限制,這是出於真實運營場景下靈活性的考慮,同一時刻相同"品類ID+屬性ID"應該只有一條記錄處於有效狀態,運營可以靈活的切換,這是程序來保障的,當然運營同學也不應該留存太多用不到的品類屬性模板。
另外提一下,因為品類有父子關系,所以通常子品類會繼承父品類的品類屬性與品類屬性值,這個不是關注的重點。
SKU
上面勾選的這些屬性,其實都是為了描述商品的一些特性,然而我們在實際購買的時候其實並不是購買商品自身,而是購買一個SKU,那么SKU是什么?和商品是什么關系?
繼續之前的編輯頁面,向下滾動進入這個區域:這里是編輯寶貝的規格,這里的規格其實背后對應的就是SKU的概念
詳細來說:
- 顏色分類和尺碼是2個品類屬性,它們和之前勾選的其他屬性相比,數據來源一樣,但是用途不同,這個后面會說。
- 選顏色時候,黑色和檸檬黃是2個品類屬性值,選尺碼的時候48.5和49.5是品類屬性值。
經過程序的排列組合,產生了如下幾種組合關系:
這里將顏色和尺碼組合后,產生了4行記錄,每一行記錄就稱作一個SKU,在購買時的效果我們應該很熟悉:
每一種尺碼和顏色的組合選定之后,價格等信息都隨之變動,這就是選擇了一個SKU,也就是我們真實購買的東西,因此尺碼和顏色分類這兩種屬性我們也稱為銷售屬性(應該給"品類屬性"表添加一個"是否為銷售屬性"),也就是控制SKU選擇的屬性。
根據上面的分析,SKU首先應該屬於一個商品,也就是上面的購買頁面代表一個商品,上面的多選按鈕可以指定一個SKU,商品和SKU之間有1:N的關系。
其次,SKU應該與屬性以及屬性值有關聯,比如:37碼-綠色,39碼-咖啡色,也就說:"尺碼屬性:37碼,顏色分類:綠色"以及"尺碼屬性:39碼,顏色屬性:咖啡色"。
SKU表
SKU ID | 主鍵 |
商品ID | 所屬的商品ID,例如:靜熙男士帆布鞋 的商品ID |
價格 | SKU的售價 |
SKU銷售屬性表(也可以打包放在sku表的一個字段里)
SKU ID | 聯合主鍵 | 一個SKU一個屬性只能對應一個值 |
屬性ID | ||
屬性值ID |
庫存
庫存系統本身是個獨立於商品庫外的系統,通常要求較高的處理能力,這里僅說明庫存應該與SKU關聯:
SKU ID | 主鍵 |
剩余庫存 | 例如:10個 |
說在最后
既然一個商品庫,需要維護那么多表,那么表之間的一致性就是很大的問題。
舉幾個例子:
- 新建商品:正常來說應該保存1個商品記錄,4個sku記錄,4個庫存記錄,以及若干其他表的信息,如果開啟數據庫事務可以保證要么都成功,要么都失敗。但是我們知道淘寶這種規模一定是分布式的mysql部署,另外,商品和庫存是2個系統,一般也不會共享數據庫,這些都導致單機事務的做法是行不通的。通常來說,都是考慮最終一致性,或者說不一致不會導致錯誤,比如:先插入庫存記錄,再插入sku記錄,最后插入商品記錄,這樣就算前一步失敗也可以保證用戶搜索不到商品,不會導致致命錯誤。
- 修改商品:修改商品,除了改商品自身的描述信息,還可能對sku進行增刪改查。如果一個商品2個人同時改,那么有可能導致2個人提交瞬間,請求各種交錯,誰都得不到想要的結果,因此一般可以考慮用數據庫鎖住商品記錄,然后對sku信息進行獨占的查詢與修改,這樣可以保證2個人串行生效,任何一方提交時發現現在的數據在編輯期間發生了變化,都可以回滾事務,重新查詢最新狀態。
另外,我們也不應該執行數據庫的delete命令來刪除屬性,屬性值,或者任何其他信息,因為極有可能已有商品對其產生了依賴,必須采用邏輯刪除,也就是標記刪除的方法,保證已有商品就算依賴了刪除的屬性也可以正常售賣,直到商家主動的重新編輯商品時重新選擇商品的屬性即可。
如果喜歡,請關注我。