三範式數據庫設計和反範式的思考(轉)

  一個人要成長到項目經理的位置,要懂的數據庫的設計原則,雖然好多東西都是理論性比較強的東西;當我們拿到一個新的需求,我們把需求從頭到尾搞清楚 後,就開始畫流程圖—>用例圖—->設計數據庫—->進入開發階段—->編碼—->測試—–>項目上線,至此一個項 目就算完成。

在這裏我們只對設計數據庫的這一塊的範例進行討論。提到範例,大家都知道第一範式,第二範式,第三範式。可是我們明白這些範式的深層含意嗎?這些範式什麼時候用,用它們有什麼好處呢?下面我們就一起帶着這些問題邊想邊讀下面的文章。

範式 :英文名稱是 Normal Form,它是英國人 E.F.Codd(關係數據庫的老祖宗)在上個世紀70年代提出關係數據庫模型後總結出來的,範式是關係數據庫理論的基礎,也是我們在設計數據庫結構過程 中所要遵循的規則和指導方法。數據庫的設計範式是數據庫設計所需要滿足的規範。只有理解數據庫的設計範式,才能設計出高效率、優雅的數據庫,否則可能會設 計出錯誤的數據庫.目前有跡可尋的共有8種範式,依次是:1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF。滿足最低要求的叫第一範 式,簡稱1NF。在第一範式基礎上進一步滿足一些要求的爲第二範式,簡稱2NF。其餘依此類推。通常所用到的只是前三個範式,即:第一範式(1NF),第 二範式(2NF),第三範式(3NF)。下面就簡單介紹下這三個範式。

◆ 第一範式(1NF): 強調的是列的原子性,即列不能夠再分成其他幾列。
考慮這樣一個表:【聯繫人】(姓名,性別,電話)
如果在實際場景中,一個聯繫人有家庭電話和公司電話,那麼這種表結構設計就沒有達到 1NF。要符合 1NF 我們只需把列(電話)拆分,即:【聯繫人】(姓名,性別,家庭電話,公司電話)。1NF 很好辨別,但是 2NF 和 3NF 就容易搞混淆。

◆ 第二範式(2NF): 首先要滿足它是1NF,另外還需要包含兩部分內容:一是表必須有一個主鍵;二是沒有包含在主鍵中的列必須完全依賴於主鍵,而不能只依賴於主鍵的一部分。
考慮一個訂單明細表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。
因爲我們知道在一個訂單中可以訂購多種產品,所以單單一個 OrderID 是不足以成爲主鍵的,主鍵應該是(OrderID,ProductID)。顯而易見 Discount(折扣),Quantity(數量)完全依賴(取決)於主鍵(OderID,ProductID),而 UnitPrice,ProductName 只依賴於 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的設計容易產生冗餘數據。
可以把【OrderDetail】表拆分爲【OrderDetail】(OrderID,ProductID,Discount,Quantity)和 【Product】(ProductID,UnitPrice,ProductName)來消除原訂單表中UnitPrice,ProductName多 次重複的情況。

◆ 第三範式(3NF): 首先是 2NF,另外非主鍵列必須直接依賴於主鍵,不能存在傳遞依賴。即不能存在:非主鍵列 A 依賴於非主鍵列 B,非主鍵列 B 依賴於主鍵的情況。
考慮一個訂單表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主鍵是(OrderID)。
其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主鍵列都完全依賴於主鍵(OrderID),所以符合 2NF。不過問題是 CustomerName,CustomerAddr,CustomerCity 直接依賴的是 CustomerID(非主鍵列),而不是直接依賴於主鍵,它是通過傳遞才依賴於主鍵,所以不符合 3NF。
通過拆分【Order】爲【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)從而達到 3NF。

問:第二範式和第三範式如何區別? 
第二範式:非主鍵列是否依賴主鍵(包括一列通過某一列間接依賴主鍵),要是有依賴關係的就是第二範式;
第三範式:非主鍵列是否是直接依賴主鍵,不能是那種通過傳遞關係的依賴的。要是符合這種就是第三範式;
問:範式的存在有什麼好處? 
範式可以避免數據冗餘,減少數據庫的空間,減輕維護數據完整性的麻煩。

範式再給我們帶來的上面的好處時,同時也伴隨着一些不好的地方:按照範式的規範設計出來的表,等級越高的範式設計出來的表越多。如第一範式可能設計 出來的表可能只有一張表而已,再按照第二範式去設計這張表時就可能出來兩張或更多張表,如果再按第三範式或更高的範式去設計這張表會出現更多比第二範式多 的表。表的數量越多,當我們去查詢一些數據,必然要去多表中去查詢數據,這樣查詢的時間要比在一張表中查詢中所用的時間要高很多。

也就是說我們所用的範式越高,對數據操作的性能越低。所以我們在利用範式設計表的時候,要根據具體的需求再去權衡是否使用更高範式去設計表。在一般的項目中,我們用的最多也就是第三範式,第三範式也就可以滿足我們的項目需求,性能好而且方便管理數據;

當我們的業務所涉及的表非常多,經常會有多表發生關係,並且我們對錶的操作要時間上要儘量的快,這時可以考慮我們使用“反範式”。反範式,故名思義,跟範式所要求的正好相反,在反範式的設計模式,我們可以允許適當的數據的冗餘,用這個冗餘去取操作數據時間的縮短。也就是用空間來換取時間,把數據冗餘在多個表中,當查詢時可以減少或者是避免表之間的關聯;

如我們現在要對一個 學校的課程表進行操作,現在有兩張表,一張是學生信息student(a_id,a_name,a_adress,b_id)表,一張是課程表 subject(b_id,b_subject),現在我們需要一個這樣的信息,把選擇每個課程的的課程名稱和學生姓名輸出來:

SQL語句爲:select  B.b_id,B.b_subject,A_a_name from student A ,subject B;

當上面的數據量不多時,我們這樣去查詢沒有問題;當我們的兩張表的數據都是在百萬級的時候,我們去查上面的信息, 問題出現了,這個查詢動不動就是幾百毫秒,甚至更慢,這樣的查詢效率根本不能滿足我們對於網頁速度的要求(一般不能超過100毫秒),怎麼辦?當然要反範式,在課程表裏面添加冗餘字段——學生姓名,這樣我們就可以通過下面的查詢達到同樣的目的:

SQL語句爲:select  b_id,b_subject,a_name from subject B;

將兩個查詢放在一起查看執行計劃,就會發現,第一個查詢開銷佔了92%,而第二個才8%,也就是說,第二個查詢比起第一個查詢,效率上優化了10倍以上,成果顯著啊。

總結:

當我們開始着手一個項目後,範式的應用是這樣的變化的:

第三範式數據庫的設計—–>當數據量越來越大,達到百萬級時,經常要對一些多表數據進行大範圍高頻率進行操作——->範式數據庫的設計———->網站的數據量再持續增長———->範式和反範式的數據庫設計

當我們的數據量非常大,目前除了對數據庫的設計改動外,還可以通過對數據層進行緩存處理。如現在使用效果顯著的Memcached ,一個分佈式的緩存系統,我們將數據庫信息以實體類的方式和圖片文件等保存在Memcached裏面,只要是可序列化的數據,經過裝箱和拆箱,都可以保存 到Memcached中並隨時可以快速的訪問到這些對象,Memcached可以解決大量數據的緩存並保持多臺Web Server得到的緩存數據是一致的。

如果大家對上面的數據庫的範式及反範式有好的見解,歡迎留言一起討論。


轉載來自:http://accpchf.iteye.com/blog/1120765 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章