數據庫設計時設計標識字段的一些思考

1、前言
  在不少應用場景中,一般須要給數據加上一些標識,以代表這條數據的某個特性,如:標識用戶的性別、標識訂單支付的渠道、標識商品的類型等等。在數據庫設計時,一般咱們會單獨用一個字段來存儲這些標識,如:可用gender字段來標識用戶的性別,其值爲「男」、「女」、「未知」這3種值中的一個;對於普通的具備有限固定的幾個值的標識,這樣天然沒有什麼問題,可是,對於一些同時具備多個屬性且變化較大的就有些不合適了,比方說,標識智能手機的商品的類型時,其類型可標識爲電子商品、娛樂商品、數碼商品等多種類型,其具備的可分類屬性衆多且不固定;這些,就是本文須要討論的:關於數據庫設計時,給數據設計標識字段時的一些思考。前端

2、常見場景、問題與解決方法
場景一:
問題與分析:
  用戶是任何系統中最爲重要的組成部分之一,在設計存儲用戶信息時,性別是用戶信息的重要組成部分,應該如何存儲呢?性別有 「男」、「女」2種狀況,若是某個用戶沒有具體性別數據的話,能夠標識爲「未知」,所以,每個用戶,性別有「男」、「女」、「未知」共3種狀況,咱們須要在數據表字段中須要存儲這3種狀況的一種(實際應用可能須要更多狀況以知足具體業務,這裏姑且以常見的3種爲例)。
思考與方法:
  方法一:在用戶信息表中,添加一個性別字段,其數據類型爲char,其值爲「未知」、「男」、「女」中的一個,當須要使用該值時(前端展現、分析統計等),直接取出使用便可。
  方法二:在用戶信息表中,添加一個字段,其數據類型爲tinyint(一個字節),其值爲 「0」、「1」、「2」,分別對應「未知」、「男」、「女」這3種狀況,其中0對應的未知爲默認值。當須要使用該值時,可針對不一樣的使用場景進行簡單的轉換,如前端需展現時可由數字轉換成相應含義文字後再展現。
  方法一的優勢是語義明確、便於前端展現,不足之處有相對佔用存儲空間多、數據傳輸更耗流量等;方法二的優勢是相對佔用存儲空間少、數據傳輸更省流量、必定程度上有利於數據統計分析,不足之處有展現時須要轉換、數字語義須要約定等。筆者我的推薦使用方法二。(換個角度看,須要轉換也帶來了必定的靈活性,如:若某個業務須要展現性別爲「保密」、「帥哥」、「靚女」,這樣咱們只需修改轉換的地方便可,而不需修改數據庫數據,這也有利於一個用戶中心爲不一樣的具體業務線提供服務)java

場景二:
問題與分析:
  電商平臺一般會劃分商品品類,如服飾類、食品類、數碼類、書籍類等等,而有的商品可能具備多個商品品類屬性,如智能手機,其既可劃爲數碼類、又可劃分爲手機類、智能設備類,常見的場景是顯示商品所屬品類、修改商品的所屬品類、查詢某個品類下有哪些商品等,在這種狀況下,該如何存儲呢?
思考與方法:
  假如平臺的商品品類共有n種,那麼,理論上某個商品的所屬品類可能的組合就有[2^n-1]種狀況,固然,實際上不會有這麼多組合,但像用戶選擇興趣愛好之類的場景可能的組合就多了,所以,上文場景一中的2種方法已再也不適用。
  可行的一個方法是:再引入一個品類關係表,用於存儲商品品類與各商品的關係。這樣的話,商品信息表自己用一個字符字段直接存儲其所屬品類信息,以知足基本需求,而操做某個品類下的商品的相關業務,則可經過品類關係表去作;一旦發生數據的變化,則同時維護這2個地方的數據。(事實上,一般的作法是,一件商品最多隻能劃分爲有限的商品品類,一我的最多隻能選擇有限個興趣愛好,如:一件商品最多隻能所屬3種品類,一我的最多隻能選5個興趣愛好)數據庫

場景三:
問題與分析:
  在電商系統中,一般會經過各類優惠的方式來促銷,如給用戶發各類優惠券、積分抵扣等,用戶提交訂單時可使用知足條件的各類優惠;那麼,如何存儲該訂單具體使用了哪些優惠信息呢?
思考與方法:
  與以上兩種狀況有所不一樣,以上的兩種場景更多的業務場景是前端的展現與業務查詢,而這種場景更可能是標識優惠以計算用戶實際所需支付金額,以及爲後續業績統計、制定促銷計劃、提升用戶活躍度等提供數據依據。
  這裏咱們舉一個具體例子來逐步分析。
實例分析:
假設某平臺爲A平臺,其平臺當前可以使用的優惠方式有如下幾種:數據庫設計

序號 優惠內容 使用條件 是否長期有效 備註
1 用戶帳戶餘額 直接抵扣現金 是 用戶充值所得(平臺獎勵吸引的充值,如:充100送10元)
2 平臺積分 100積分抵扣1元 是 經過參與平臺活動、購物行爲積累獲取
3 平臺幣(A幣) 直接抵扣,1個幣抵扣1元 是 平臺獎勵(相似Q幣之類的概念,姑且叫A幣)
4 滿減卷5元 滿100減5元 否 平臺活動促銷發放
5 免郵費 訂單總金額符合條件便可 是 平臺單筆訂單總金額滿199元免郵費
  用戶下單時,只要是知足各優惠的使用條件,就能夠疊加使用各類優惠,那麼,數據庫如何存儲用戶具體使用了哪些優惠呢?(實際各類促銷優惠可能更多,尤爲是各類優惠券,這裏姑且以5種舉例)
分析:
  在這個業務場景中,用戶下單時最多能夠同時使用5種優惠抵扣方式,用戶可能使用的優惠組合共有 2^5-1=31 種,在最終計算用戶的訂單實際須要支付的金額時,如何標識並存儲用戶到底使用了哪一種優惠組合呢?
  若是單獨用一個普通標識字段來標識存儲,實現起來是比較簡單,可是其須要標識的組合種類實在有點多,不太利於編碼與後續擴展,試想,若是新加了一種優惠類型,其須要添加多少種組合標識啊,且呈指數式爆長,這種方式顯然不太合理。若是採用另外引入一張關聯表的方式,專門用一張關聯表來存儲訂單使用的優惠組合信息,每使用一種優惠就添加一條關聯記錄,相比單獨使用普通字段標識,這在必定程度上減小了設置標識的繁瑣性,增長了靈活性(每多使用一種優惠就添加一條關聯記錄),可是,同時也帶來了另外一些問題,其中主要問題是:新增一張關聯表後,數據維護起來麻煩。在互聯網場景下,數據量一般是很是大的,像訂單數據通常都須要進行數據庫sharding,以應對數據量暴漲後數據庫的讀寫性能瓶頸,增長系統的水平擴展能力。所以,另外增長一張數據量是訂單數據自己數據量幾倍的關聯表也顯然不太合適。
  那麼,有沒有一種方式既方便標識存儲又方便擴展呢?
  咱們試着以一種「特殊標識位」的方式來實現,具體思路以下:
  a、定義一個標識位 mask 用於標識存儲優惠信息;
  b、mask存儲的值並非直接存一、二、3之類的十進制數字,而是存儲一個二進制數轉化後的十進制數,這些一、二、3之類的優惠數字表示佔二進制數的第幾位(從右至左數);
  c、具體數據的存儲、讀取判斷經過工具類轉換進行。
  例:
  int 數據類型4個字節,共32位,除去符號位,可用於標識的位數有31位,即最多能夠標識31種優惠狀況,而若是是long數據類型的話,能標識的種類就更多了。
  在以上共5種優惠方式場景中,可按以下標識存儲:
  
  
  說明:若用戶使用了優惠1,則使用二進制數 00000001 標識,若使用了優惠2,則使用二進制數 00000010 標識,存儲到DB時,轉換成對應十進制數分別對應一、2;若同時使用了優惠1、優惠2,則使用二進制數 00000011 標識,最終存儲到DB的對應十進制數是3。其它優惠項,所佔的二進制位依次類推。
  代碼示例以下:
DiscountEnum.java
---------------------
做者:對門山上
來源:CSDN
原文:https://blog.csdn.net/cndmss/article/details/54232738工具

相關文章
相關標籤/搜索