之前的兩篇筆記中談到了從單庫擴展到多庫以承載更多的請求量以及單庫(表)拆分成多庫(表),打破單庫的性能瓶頸。
這都是為了應對大數據量下的措施。
然而,除卻數據量外,還有一個極其影響單庫性能的因素——數據的組織方式。
對于關系型數據庫,我們可以嘗試在一定程度上改變數據的組織方式,即反范式化(Denormalization)
關于范式
可以參考以前做的筆記:
《MySQL——數據表設計三大范式》
設計范式相當于數據層的設計模式,對數據表進行解耦,使單表信息更加內聚,彼此邊界分明,依賴關系更加清晰.
試想,如果相同的信息在多行中重復出現,不相干的信息也湊在同一張表中,就很容易出現一些異常情況:
- 更新異常:只更新單行,就會出現邏輯上的不一致
- 插入異常:無法只插入部分信息,除非讓其它列先留空
- 刪除異常:刪除部分信息的同時,可能會波及其它無關信息
范式化的弊端
在這些設計范式的約束下,相關聯的信息被存儲到了不同的邏輯表中,以致于經常需要多表聯查(join操作),關系越復雜,連表查詢越慢。例如分成客戶表和訂單表和商戶表,當我們要執行的操作與這張表的部分信息都有關時,就需要進行多表join。
那么,有辦法能改善查詢性能嗎?
- 允許 DBMS 存儲額外的冗余信息,例如索引視圖(indexed views)、物化視圖(materialized views),但仍遵從設計范式
- 增加冗余數據,減少join操作,打破設計范式(即反范式化)
反范式化
所謂反范式化,是一種針對遵從設計范式的數據庫(關系模式)的性能優化策略。
反范式化不等于非范式化(Unnormalized form),反范式化一定發生在滿足范式設計的基礎之上。前者相當于先遵守所有規則,再進行局部調整,故意打破一些規則,而后者全然不顧規則。
反范式化通過增加冗余數據或對數據進行分組,犧牲一部分寫入性能,換取更高的讀取性能:
在設計范式的約束下,數據表中沒有冗余信息(某個數據只存放在某張表的某個單元格中),為了得到某個數據可能需要一系列的跨表查詢,因而讀操作性能不佳,但寫操作很快,因為更新數據時只需要修改一處。
反范式化就是要打破這種約束,把某些數據在不同的地方多放幾份,以加快數據檢索速度。
具體操作如下:
- 存一些派生數據:類似于往 Redux Store 中塞計算屬性,把需要頻繁重復計算的結果存起來,例如在一對多關系中,把“多”的數量作為“一”的屬性存儲起來
- 預先連接(pre-joined)生成匯總表:把需要頻繁join的表提前join好
- 采用硬編碼值:把依賴表中的常量值(或者不經常變化的值)直接硬編碼到當前表中,從而避免join操作
- 把詳情信息納入主表中:對于數據量不大的詳情表,可以把全部/部分詳情信息塞到主表中,以避免join操作
反范式化的代價
- 失去了數據完整性保障:打破范式,意味著之前通過范式化解決的更新、插入、刪除異常問題又將重新冒出來,也就是說,冗余數據的一致性要靠 DBA 自己來保證,而不像索引視圖等由 DBMS 來保證
- 犧牲了寫入速度:由于反范式化引入了冗余數據,更新時要修改多處,但大多數場景都是讀密集的,寫入慢一點問題不大
- 浪費了存儲空間:存儲了不必要的冗余數據,自然會浪費一些存儲空間,但空間換時間一般是可接受的(畢竟內存、硬盤等資源已經相對廉價了)
參考
http://www.ayqy.net/blog/database-denormalization/