MySQL數據庫表的設計和優化(下)

2、基於單表設計的多表設計原則:
(1)表關係:
一)一對一關係:
定義:
在這種關係中,關係表的每一邊都只能存在一個記錄。每一個數據表中的關鍵字在對應的關係表中只能存在一個記錄或者沒有對應的記錄。這種關係和一對配偶之間的關係很是類似——要麼你已經結婚,你和你的配偶只能有一個配偶,要麼你沒有結婚沒有配偶。大多數的一對一的關係都是某種商業規則約束的結果,而不是按照數據的天然屬性來獲得的。若是沒有這些規則的約束,你一般能夠把兩個數據表合併進一個數據表,並且不會打破任何規範化的規則。
一對一關係又分爲:一對一外鍵關聯和一對一主鍵關聯。
一對一主鍵關聯:要求兩個表的主鍵必須徹底一致,經過兩個表的主鍵創建關聯關係。
能夠看到下圖,很明顯的,班級編號做爲主鍵的話,就是一個主鍵關聯了。

                        

 



一對一外鍵關聯:
下面又很明顯看到,以班主任ID做爲外鍵關聯起來的一個表。




二)一對多關係(多對一):
定義:
主鍵數據表中只能含有一個記錄,而在其關係表中這條記錄能夠與一個或者多個記錄相關,也能夠沒有記錄與之相關。這種關係相似於你和你的父母之間的關係。你只有一位母親,可是你母親能夠有幾個孩子。
下圖能夠看到:一對多-班級表有多個學生;多對一-多個學生屬於一個班級。

                     

   
三)多對多關係:
定義:
兩個數據表裏的每條記錄均可以和另外一個數據表裏任意數量的記錄(或者沒有記錄)相關。例如,若是你有多個兄弟姐妹,這對你的兄弟姐妹也是同樣(有多個兄弟姐妹),多對多這種關係須要引入第三個數據表,這種數據表稱爲聯繫表或者鏈接表,由於關係型系統不能直接實現這種關係。

在RDBMS中,必須使用中間表來表示多對多的關係。中間表咱們能夠分紅兩種,一種是純粹表示關係的中間表,一種是表示中間實體的中間表。

純粹表示關係的中間表很簡單,只須要兩列:AID和BID,AID之外鍵關聯到A表的主鍵,BID之外鍵關聯到B表的主鍵,而後這兩個列組成聯合主鍵。這個中間表純粹是表示多對多關係而存在,在業務上不會有對應的實體與之對應。好比前面提到的學生和課程的關係,若是咱們只須要知道哪些學生上哪些課,哪些課有哪些學生選,不須要有更多的信息的狀況下,咱們就能夠創建「學生課程」中間表,裏面只有學生ID和課程ID兩個字段。

             

        中間實體是在純粹的中間關係表的基礎上,加上了更多的屬性,從而造成了一個新的實體。好比上面提到的學生和課程的關係,若是咱們須要記錄學生選課的時間、學生選擇這門課程後的考試成績,那麼咱們就像創建一個「選課」實體,
該實體具備以下屬性:
選課ID,主鍵
學生ID,與學生表作外鍵關聯
課程ID,與課程表作外鍵關聯
選課時間,DateTime類型
考試成績,記錄選修該課程後考試的最終成績

注意:
一)外鍵與索引:
外鍵是一種約束,與索引的概念不同,只是大多數狀況下,咱們創建外鍵時,都會在外鍵列上創建對應的索引。外鍵的存在會在每一次數據插入、修改時進行約束檢查,若是不知足外鍵約束,則禁止數據的插入或修改,這必然帶來一個問題,就是在數據量特別大的狀況下,每一次約束檢查必然致使性能的降低。索引其實也有相似的問題,索引若是建多了,那麼在插入刪除修改數據時也要去維護對應的索引,因此索引的存在也會致使數據操做變慢。
不過外鍵與索引的優勢不一樣,外鍵只是保證數據的一致性,並不能給系統性能帶來任何好處,因此因爲外鍵致使的插入數據變慢會隨着數據量的增加而愈來愈嚴重。而索引的目的是爲了檢索數據更快,維護數據時致使的索引數據的變動,對性能的影響不會像外鍵那樣隨着數據量增加而變得嚴重(固然大數量時的索引樹維護會比小數據量的索引樹維護更麻煩,但至少不是像外鍵那樣)。
出於性能的考慮,若是咱們的系統徹底由咱們開發的程序使用,而不須要提供數據庫給其餘應用系統寫入數據,並且對性能要求較高,那麼咱們能夠考慮在生產環境中不使用外鍵,只須要創建可以提升性能的索引。因爲整個數據庫的操做都是由咱們開發的程序來完成的,因此咱們程序能夠在開發過程當中作好各方面的一致性檢查,保證操做的數據是知足外鍵約束的,而不須要真正的存在這樣一個外鍵約束。怎麼作到這一點呢,首先,咱們在創建數據庫時有多個腳本,包括建立表、建立初始化數據、建立索引、建立外鍵等,咱們在開發和測試環境中,都把這些腳本運行了,以使開發測試環境中的數據庫是完整的,通過大量測試保證應用程序可以維護數據之間的約束的狀況下,那麼咱們在生產時,並不須要運行建立外鍵這個腳本文件,只須要建立表、初始化數據、建立索引等便可。
二)創建關係
在開始着手考慮創建關係表之間的關係以前,你可能須要對數據很是熟悉。只有在熟悉數據以後,關聯會比你剛開始的時候更明顯。你的數據庫系統依賴於在兩個數據表中找到的匹配值來創建關係。
進行匹配的值都是主鍵和外鍵的值。(關係模型不要求一個關係必須對應的使用一個主鍵來肯定。你可使用數據表中的任何備選關鍵字來創建關係,可是使用主鍵是你們都已經接受的標準。)主鍵(primary key)惟一的識別表中的每一個記錄。而外鍵(foreign key)只是簡單的將一個數據表中的主鍵存放在另一個數據表中。一樣地,對於你來講也不須要作太多的工做——只是簡單地將主鍵加到關係表中,並將其定義爲外鍵。

(2)分表原則:
分表主要目的是爲突破單節點數據庫服務器的 I/O 能力限制,解決數據庫擴展性問題。 同時分表分庫等思想也將引出之後的數據庫集羣,主從複製、讀寫分離方案…

爲何咱們要分表分區???
平常開發中咱們常常會遇到大表的狀況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,致使數據庫在查詢和插入的時候耗時太長,性能低下,若是涉及聯合查詢的狀況,性能會更加糟糕。分表和表分區的目的就是減小數據庫的負擔,提升數據庫的效率,一般點來說就是提升表的增刪改查效率。

(一)表拆分方式:
1)垂直切分:

定義:
把主鍵和一些數據表的列放在一個表中,而後把主鍵和另外一些數據表的列放在一個表中。
若是一個表的某些列經常使用,另外一些不經常使用,則能夠採用垂直拆分。垂直拆分可使數據行變小,一個數據頁就能夠存放更多的數據,在查詢時候能夠減小I/O次數。其缺點是須要管理冗餘列,查詢全部數據時候須要join查找。
優勢:
使得行數據變小,一個數據塊(Block)就能存放更多的數據,在查詢時就會減小I/O次數(每次查詢時讀取的Block 就少)。
能夠達到最大化利用Cache的目的。

缺點:
表垂直分割後,主碼(主鍵)出現冗餘,須要管理冗餘列
會引發錶鏈接JOIN操做(增長CPU開銷)須要從業務上規避

2)水平拆分(分表,分區)–按表中某一字段值的範圍劃分:


定義:
根據列的範圍值進行合理切分,放在多個獨立的表或分區中。

適用場景:
表很大,分割後能夠下降查詢時候須要讀取的數據和索引的頁數,同時下降索引的層數,提升查詢速度。
表中的數據是獨立的,例如表中分別記錄各個地區的數據或不一樣時期的數據,特別是有些數據經常使用,而另外一些數據不經常使用。
須要把數據放在多個存儲介質上。
須要把歷史數據和當前的數據拆分開。

例子:
當伴隨着某一個表的數據量愈來愈大,以致於不能承受的時候,就須要對它進行進一步的切分。一種選擇是根據key 的範圍來作切分,譬如ID 爲 1-10000的放到表A上,ID 爲10000~20000的放到表B。這樣的擴展就是可預見的。另外一種是根據某一字段值來劃分,譬如根據用戶名的首字母,若是是A-D,就屬於表A,E-H就屬於表B。這樣作也存在不均衡性,當某個範圍超出了單點所能承受的範圍就須要繼續切分。還有按日期切分等等。

可使用Mrg_Myisam引擎實現水平分表。
優勢:
單表大小可控,自然水平擴展。下降在查詢時須要讀的數據和索引的頁數,同時也下降了索引的層數,加快了查詢速度。

缺點:
沒法解決集中寫入瓶頸的問題。同時,水平分割會給應用增長複雜度,它一般在查詢時須要多個表名,查詢全部數據須要union操做。在許多數據庫應用中,這種複雜性會超過它帶來的優勢,由於只要索引關鍵字不大,則在索引用於查詢時,表中增長兩到三倍數據量,查詢時也就增長讀一個索引層的磁盤次數。

3)散列庫表(基於hash算法的切分):
定義:
表散列與水平分割類似,但沒有水平分割那樣的明顯分割界限,採用Hash算法把數據分散到各個分表中, 這樣IO更加均衡。通常採用mod來切分,一開始肯定切分數據庫的個數,經過hash取模來決定使用哪臺。這種方法可以平均地來分配數據,可是伴隨着數據量的增大,須要進行擴展的時候,這種方式沒法作到在線擴容。每增長節點的時候,就須要對hash 算法從新運算。
咱們會按照業務或者功能模塊將數據庫進行分離,不一樣的模塊對應不一樣的數據庫或者表,再按照必定的策略對某個頁面或者功能進行更小的數據庫散列,好比用戶表,按照用戶ID進行表散列,散列128張表,則應就可以低成本的提高系統的性能而且有很好的擴展性

優勢:
數據分佈均勻

缺點:
數據遷移的時候麻煩,不能按照機器性能分攤數據

(二)在瞭解完分表了,咱們先來理解區分分區與分表吧。
分區:
定義:
分區和分表類似,都是按照規則分解表。不一樣在於分表將大表分解爲若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,能夠是同一塊磁盤也能夠在不一樣的機器。分區後,表面上仍是一張表,但數據散列到多個位置了。app讀寫的時候操做的仍是大表名字,db自動去組織分區的數據。

分表定義:
分表是將一個大表按照必定的規則分解成多張具備獨立存儲空間的實體表,咱們能夠稱爲子表,每一個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表能夠分佈在同一塊磁盤上,也能夠在不一樣的機器上。app讀寫的時候根據事先定義好的規則獲得對應的子表名,而後去操做它。

mysql分表和分區有什麼聯繫呢?:
1.都能提升mysql的性高,在高併發狀態下都有一個良好的表現。
2.分表和分區不矛盾,能夠相互配合
,對於那些大訪問量,而且表數據比較多的表,咱們能夠採起分表和分區結合的方式(若是merge這種分表方式,不能和分區配合的話,能夠用其餘的分表試),訪問量不大,可是表數據不少的表,咱們能夠採起分區的方式等。

3.分表技術是比較麻煩的,須要手動去建立子表,app服務端讀寫時候須要計算子表名。採用merge好一些,但也要建立子表和配置子表間的union關係。
4.表分區相對於分表,操做方便,不須要建立子表。
(三)表拆分建議:(針對大系統)
其實這點沒有明確的判斷標準,比較依賴實際業務狀況和經驗判斷。通常MySQL單表1000W左右的數據是沒有問題的(前提是應用系統和數據庫等層面設計和優化的比較好)。

1)對記錄多的表進行拆分。(幾百-上千萬級別的表)
2)須要拆分的表分爲動態表和相對靜態表。動態表拆分到不一樣庫,靜態表存在於公共庫。從公共庫同步到分庫。實現表的鏈接。
3)按照年、月、地域等來分割,或者根據時間範圍、和很固定又清晰的字段值範圍等,具備肯定的分割標誌來分割。
相關文章
相關標籤/搜索