面試官:什么是數據庫范式?什么是反范式?有什么優缺點?
設計數據庫表結構時,數據庫范式是我們經常考慮的策略。但往往很多時候,我們會違反范式,以獲得更好的性能。今天來聊一聊這個話題。
1.范式
第一范式
表中字段是原子的,不可以再拆分。一個經典的例子就是地址字段記錄了省市區:
id | 地址 |
1 | 北京市海淀區 |
如果要符合范式,可以對地址進行拆分。
id | 省 | 市 | 區 |
1 | 北京市 | 北京市 | 海淀區 |
當然,這里只是為了舉例說明,實際項目中,會使用標準的行政地區碼來進行保存。
第二范式
首先要滿足第一范式,在此基礎上主鍵以外的字段必須完全依賴主鍵,不能依賴其他字段。比如下面的訂單表:
訂單id | 產品id | 產品數量 | 購買價格 | 訂單金額 |
100 | p201 | 2 | 20 | 130 |
100 | p202 | 3 | 30 | 130 |
訂單表如果單獨用“訂單id”不能標記唯一記錄,只能使用“訂單id+產品id”做聯合主鍵,但是訂單金額這個字段跟“產品id”沒有關系,這就不符合第二范式。可以對訂單表進行優化,去除“訂單金額”:
訂單id | 產品id | 產品數量 | 購買價格 |
100 | p201 | 2 | 20 |
100 | p202 | 3 | 30 |
訂單金額表:
訂單id | 訂單金額 | 幣種 |
100 | 130 | RMB |
查詢的時候使用訂單id 做 JOIN 查詢。
第三范式
首先要滿足第二范式,在此基礎上,表中的字段不能間接依賴主鍵,也就是需要通過依賴傳遞來對主鍵形成依賴。再看下面的訂單表,主鍵是訂單id:
訂單id | 用戶id | 用戶姓名 |
100 | C10 | tom |
101 | C11 | json |
用戶姓名不能直接依賴主鍵“訂單id”,而是通過“用戶id”間接依賴的,這就不符合第三范式。可以拆分出用戶表,查詢的時候使用用戶id 進行 JOIN。 訂單表:
訂單id | 用戶id |
100 | C10 |
101 | C11 |
用戶表:
用戶id | 用戶姓名 |
C10 | tom |
C11 | json |
BCNF 范式
也稱 BC 范式,它是在滿足第三范式的基礎上,不允許一個表中存在兩個可以做主鍵的字段。比如下面這個倉庫表:
倉庫id | 管理員id | 存儲商品id | 存儲商品數量 |
S100 | M10 | P200 | 200 |
S100 | M10 | P300 | 230 |
如果一個倉庫只能由一個管理員管理,而一個管理員也只能管理一個倉庫,那主鍵可以選擇{倉庫id,存儲商品id},也可以選擇{管理員id,存儲商品id}。這就不符合 BCNF 范式。 可以把上表拆分出兩個表,倉庫管理表:
倉庫id | 管理員id |
S100 | M10 |
S100 | M10 |
倉庫表:
倉庫id | 存儲商品id | 存儲商品數量 |
S100 | P200 | 200 |
S100 | P300 | 230 |
4NF 范式
4NF 范式是在第三范式的基礎上,不允許表中字段由多對多的依賴。比如下面這個表符合第三范式,但是訂單id 和產品id 存在多對多的關系。
訂單id | 產品id | 產品名稱 |
100 | p201 | 產品1 |
100 | p202 | 產品2 |
可以拆分成三個表,訂單產品關系表:
訂單id | 產品id |
100 | p201 |
100 | p202 |
訂單表:
訂單id | 訂單金額 |
100 | 130 |
產品表:
產品id | 產品名稱 |
p201 | 產品1 |
p202 | 產品2 |
查詢的時候做三張表的 JOIN 查詢。
2.優缺點
從上面范式的介紹可以看出,范式是數據庫設計的規約,主要有以下優點:
- 減少存儲空間:同一個實體的屬性只在表中存儲一次,減少了數據冗余,節省了存儲空間;
- 寫數據性能高:每個屬性的插入、更新通常只需要操作一張表,操作的數據集小,效率更高;
- 數據完整性:遵守范式設計,表中需要保存關聯實體的主鍵,通過主鍵關聯來保證數據完整性。
- 去重操作少:沒有冗余數據,也就很少會用到類似 distinct 和 group by 這樣的耗時語句。
但過度遵循范式設計,也會存在一些缺點:
- 查詢性能受到影響:查詢通常需要 JOIN 多個表,JOIN 的表數量較多時,JOIN 語句會成為性能瓶頸;
- SQL 語句復雜度升高:多張表 JOIN 往往使查詢語句可讀性差,遇到重構、遷移之類的工作,會帶來很多額外工作量;
- 對索引依賴更多:為了提高 JOIN 語句性能,往往需要在連接字段上建立索引。
因為遵循范式可能存在的缺點,在實際設計和開發中,我們往往會引入反范式,通過增加數據冗余,將不遵循范式但是需要的字段放到一個表中,通過增加冗余來避免復雜的 JOIN,通過對冗余字段增加索引來提高查詢效率。核心思想也是時間換空間。
反范式帶來的優點是簡化查詢語句,提高 SQL 執行效率,對并發讀的場景更加合適。
但在寫多的場景下,也會存在一些問題,比如因為要寫多張表,增刪改操作更復雜 ,很容易造成鎖競爭,降低寫入性能。同時也更容易導致數據不一致,維護難度增加。
3.使用建議
在我們的實際項目開發中,一般都使用混合范式。
對 OLTP(聯機事務處理)類型的使用場景,比如電商、ERP 等寫入比較多的系統,可以考慮使用范式設計。
而對于 OLAP(聯機分析處理)的使用場景,比如報表、數倉等,需要處理復雜查詢,都是讀取操作,可以考慮采用反范式設計。通常使用 ELT 工具把業務數據從關系型數據庫抽取到湖倉,在湖倉構建反范式化的數據模型,用于業務數據查詢和報表生成。
































