對於任何給定的資料通常都有很多種表示方法,從完全的正規化化到完全的反正規化化,以及兩者的折中。在正規化化的資料庫中,每個事實資料會出現並且只出現一次。相反,在反正規化化的資料庫中,資訊是冗餘的,可能會儲存在多個地方。
下面以經典的「雇員,部門,部門領導」的例子開始:
這個schema的問題是修改資料時可能發生不一致。假如say brown接任accounting部門的領導,需要修改多行資料來反應這個變化,這是很痛苦的事並且容易引入錯誤。如果「jones」這一行顯示部門的領導跟「brown」這一行的不一樣,就沒有辦法知道哪個是對的。此外,這個設計在沒有雇員資訊的情況下就無法表示乙個部門——如果我們刪除了所有accounting部門的雇員,我們就失去了關於這個部門本身的所有記錄。要避免這個問題,我們需要對這個表進行正規化化,方式是拆分雇員和部門項。拆分以後可以用下面兩張表分別來儲存雇員表:
和部門表:
這樣設計的兩張表符合第二正規化,在很多情況下做到這一步已經足夠好了。然而,第二正規化只是許多可能的正規化中的一種。
當為效能問題而尋求幫助時,經常會被建議對schema進行正規化化設計,尤其是寫密集的場景。這通常是個好建議。因為下面這些原因,正規化化通常能夠帶來好處:
正規化化設計的schema的缺點是通常需要關聯。稍微複雜一些的查詢語句在符合正規化的schema上都可能需要至少一次關聯,也許更多。這不但代價昂貴,也可能使一些索引策略無效。例如,正規化化可能將列存放在不同的表中,而這些列在乙個表中本可以屬於同乙個索引。
反正規化化的schema因為所有資料都在一張表中,可以很好的避免關聯。如果不需要關聯表,則對於大部分查詢最差的情況——即使表沒有使用索引——是全表掃瞄。當資料比記憶體大時這可能比關聯要快得多,因為這樣避免了隨機i/o。
單獨的表也能使用更有效的索引策略。假設有乙個**,允許使用者傳送訊息,並且一些使用者是付費使用者。現在想檢視付費使用者最近的10條資訊。如果是正規化化的結構並且索引了傳送日期欄位published,這個查詢也許看起來像這樣:
要更有效率地執行這個查詢,mysql需要掃瞄message表的published欄位的索引。對於每一行找到的資料,將需要到user表裡檢查這個使用者是不是付費使用者。如果只有一小部分使用者是付費賬戶,那麼這是效率低下的做法。
另一種可能的執行計畫是從user表開始,選擇所有付費使用者,獲得他們所有的資訊,並且排序。但這可能更加糟糕。
主要問題是關聯,使得需要在乙個索引中又排序又過濾。如果採用反正規化化組織資料,將兩張表的字段合併一下,並且增加乙個索引(account_type,published),就可以不通過關聯寫出這個查詢。這將非常高效:
正規化化和反正規化化的schema各有優劣,怎麼選擇最佳的設計?
事實是,完全的正規化化和完全的反正規化化schema都是實驗室裡才有的東西:在真實世界中很少會這麼極端的使用。在實際應用中經常混用,可能使用部分正規化化的schema、快取表,以及其他技巧。
最常見的反正規化化資料的方法是複製或者快取,在不同的表中儲存相同的特定列。在mysql5.0和更新版本中,可以使用觸發器更新快取值,這使得實現這樣的方案變得更簡單。
在我們的**例項中,可以在user表和message表中都儲存account_type欄位,而不用完全的反正規化化。這避免了完全反正規化化的插入和刪除問題,因為即使沒有訊息的時候也絕不會丟失使用者的資訊。這樣也不會把user_message表搞得太大,有利於高效的獲取資料。
但是現在更新使用者的賬號型別的操作代價就高了,因為需要同時更新兩張表。至於這會不會是乙個問題,需要考慮更新的頻率以及更新的市場,並和執行select查詢的頻率進行比較。
另乙個從父表冗餘一些資料到子表的理由是排序的需要。例如,在正規化化的schema裡通過作者的名字對訊息做排序的代價將會非常高,但是如果在message表中快取author_name欄位並且建好索引,則可以非常高效的完成排序。
快取衍生值也是有用的。如果需要顯示每個使用者發了多少訊息(像很多論壇做的),可以每次執行乙個昂貴的子查詢來計算並顯示它;也可以在user表中建乙個num_messages列,每當使用者發新訊息時更新這個值。
mysql的正規化 Mysql正規化與反正規化的利弊
mysql正規化與反正規化的利弊 一 三大正規化 第一正規化 1nf是對屬性的原子性,要求屬性具有原子性,不可再分解 第一正規化是最基本的正規化。如果資料庫表中的所有字段值都是不可分解的原子值,就說明該資料庫表滿足了第一正規化。資料庫表的每一列都是不可分割的原子資料項,而不能是集合,陣列,記錄等非原...
mysql 正規化化 Mysql正規化與反正規化
第一正規化 1nf 第一正規化,強調屬性的原子性約束,要求屬性具有原子性,不可再分解。第二正規化 2nf 第二正規化,強調記錄的唯一性約束,表必須有乙個主鍵,並且沒有包含在主鍵中的列必須完全依賴於主鍵,而不能只依賴於主鍵的一部分。舉個例子 版本表 版本編碼,版本名稱,產品編碼,產品名稱 其中主鍵是 ...
MySQL三正規化與反三正規化
目錄反三正規化 對於三正規化和反三正規化的選擇 參考cs notes 資料庫 資料庫系統原理.html 正規化 屬性不可再分 即表中的字段不能再拆分 例 乙個name屬性可以分為firstname屬性和lastname屬性,則不符合第一正規化。表中有鍵碼,非主屬性完全依賴鍵碼 即表中的非主屬性完全依...