本文首先探討下數據庫設計的三大范式,因為范式只是給出了數據庫設計的原則,並沒有告訴我們實際操作中應該怎樣操作,應該注意什么,所以我們還會談下實際工作中需要注意的具體操作問題。
三大范式
首先放出三大范式內容,然后再進行解釋:
第一范式:確保每列的原子性.,如果每列(或者每個屬性)都是不可再分的最小數據單元(也稱為最小的原子單元),則滿足第一范式.;(只要是關系型數據庫都滿足1NF)
第二范式:如果一個關系滿足第一范式,並且除了主鍵以外的其它列,都依賴於該主鍵,則滿足第二范式.;
第三范式:如果一個關系滿足第二范式,並且除了主鍵以外的其它列都不依賴於主鍵列,則滿足第三范式。
一范式就是屬性不可分割。屬性是什么?就是表中的字段。不可分割的意思就按字面理解就是最小單位,不能再分成更小單位了。這個字段只能是一個值,不能被拆分成多個字段。不過能不能分割並沒有絕對的答案,看需求,也就是看你的設計目標而定。
舉例:學生信息組成學生信息表,有姓名、年齡、性別、學號等信息組成。姓名不可拆分吧?所以可以作為該表的一個字段。但我要說這個表要在國外使用呢?人家姓和名要分開,都有特別的意義,所以姓名字段是可拆分的,分為姓字段和名字段。簡單來說,一范式是關系數據庫的基礎,但字段是否真的不可拆分,根據你的設計目標而定。
二范式就是要有主鍵,要求其他字段都依賴於主鍵。為什么要有主鍵?沒有主鍵就沒有唯一性,沒有唯一性在集合中就定位不到這行記錄,所以要主鍵。其他字段為什么要依賴於主鍵?因為不依賴於主鍵,就找不到他們。如果有同學不理解依賴這個詞,可以勉強用“相關”這個詞代替,也就是說其他字段必須和它們的主鍵相關。因為不相關的東西不應該放在一行記錄里。
舉例:學生信息組成學生表,姓名可以做主鍵么?不能!因為同名的話,就不唯一了,所以需要學號這樣的唯一編碼才行。那么其他字段依賴於主鍵是什么意思?就是“張三”同學的年齡和性別等字段,不能存儲別人的年齡性別,必須是他自己的,因為張三的學號信息就決定了,這行記錄歸張三所有,不能給無關人員使用。
三范式就是要消除傳遞依賴,方便理解,可以看做是“消除冗余”。消除冗余應該比較好理解一些,就是各種信息只在一個地方存儲,不出現在多張表中。比如說大學分了很多系(中文系、英語系、計算機系……),這個系別管理表信息有以下字段組成:系編號,系主任,系簡介,系架構。那么再回到學生信息表,張三同學的年齡、性別、學號都有了,我能不能把他的系編號,系主任、系簡介也一起存着?如果你問三范式,當然不行,因為三范式不同意。因為系編號,系主任、系簡介已經存在系別管理表中,你再存入學生信息表,就是冗余了。三范式中說的傳遞依賴,就出現了。這個時候學生信息表中,系主任信息是不是依賴於系編號了?而這個表的主鍵可是學號啊!所以按照三范式,處理這個問題的時候,學生表就只能增加一個系編號字段。這樣既能根據系編號找到系別信息,又避免了冗余存儲的問題。
還有要強調的就是所謂的范式,是用來學習參考的,設計的時候根據情況,未必一定要遵守。因為在數據庫數據量特別大,並且訪問並發也大的情況下,可能要采用反范式設計來提高數據庫響應速度,不過這里不在過多討論,有興趣的同學可以到這個網址查看具體內容,該文檔針對mysql的優化做了詳細說明:https://wenku.baidu.com/view/aa43ecc3aa00b52acfc7ca94.html
實際操作規范(mysql)
-
表設計
-
庫名、表名、字段名必須使用小寫字母,“_”分割,且名稱長度不超過12個字符並且要做到見名知意。
-
建議使用InnoDB存儲引擎。
-
存儲精確浮點數必須使用DECIMAL替代FLOAT和DOUBLE。
-
建議使用UNSIGNED存儲非負數值。
-
建議使用INT UNSIGNED存儲IPV4。
-
整形定義中不添加長度,比如使用INT,而不是INT(4)。
-
使用短數據類型,比如取值范圍為0-80時,使用TINYINT UNSIGNED。
-
不建議使用ENUM類型,使用TINYINT來代替。
-
盡可能不使用TEXT、BLOB類型。
-
VARCHAR(N),N表示的是字符數不是字節數,比如VARCHAR(255),可以最大可存儲255個漢字,需要根據實際的寬度來選擇N。
-
VARCHAR(N),N盡可能小,因為MySQL一個表中所有的VARCHAR字段最大長度是65535個字節,進行排序和創建臨時表一類的內存操作時,會使用N的長度申請內存。
-
表字符集選擇UTF8。
-
使用VARBINARY存儲變長字符串。
-
存儲年使用YEAR類型,存儲日期使用DATE類型,存儲時間(精確到秒)建議使用TIMESTAMP類型,因為TIMESTAMP使用4字節,DATETIME使用8個字節。
-
建議字段定義為NOT NULL。
-
將過大字段拆分到其他表中。
-
禁止在數據庫中使用VARBINARY、BLOB存儲圖片、文件等。
-
索引
-
索引名稱必須使用小寫,非唯一索引必須按照“idx_字段名稱_字段名稱[_字段名]”進行命名,唯一索引必須按照“uniq_字段名稱_字段名稱[_字段名]”進行命名。
-
索引中的字段數建議不超過5個。
-
單張表的索引數量控制在5個以內。
-
唯一鍵由3個以下字段組成,並且字段都是整形時,使用唯一鍵作為主鍵。
-
沒有唯一鍵或者唯一鍵不符合4中的條件時,使用自增(或者通過發號器獲取)id作為主鍵。
-
唯一鍵不和主鍵重復。
-
索引字段的順序需要考慮字段值去重之后的個數,個數多的放在前面。
-
ORDER BY,GROUP BY,DISTINCT的字段需要添加在索引的后面。
-
使用EXPLAIN判斷SQL語句是否合理使用索引,盡量避免extra列出現:Using File Sort,UsingTemporary。
-
UPDATE、DELETE語句需要根據WHERE條件添加索引。
-
不建議使用%前綴模糊查詢,例如LIKE “%weibo”。
-
合理創建聯合索引(避免冗余),(a,b,c)相當於 (a) 、(a,b) 、(a,b,c)。
-
合理利用覆蓋索引。
-
SQL變更需要確認索引是否需要變更並通知DBA。
-
SQL語句
-
SQL語句中IN包含的值不應過多。
-
UPDATE、DELETE語句不使用LIMIT。
-
WHERE條件中必須使用合適的類型,避免MySQL進行隱式類型轉化。
-
SELECT語句只獲取需要的字段。
-
SELECT、INSERT語句必須顯式的指明字段名稱,不使用SELECT *,不使用INSERTINTO table。
-
使用SELECT column_name1, column_name2 FROM table WHERE[condition]而不是SELECT column_name1 FROM table WHERE[condition]和SELECT column_name2 FROM table WHERE [condition]。
-
WHERE條件中的非等值條件(IN、BETWEEN、<、<=、>、>=)會導致后面的條件使用不了索引。
-
避免在SQL語句進行數學運算或者函數運算,容易將業務邏輯和DB耦合在一起。
-
INSERT語句使用batch提交(INSERT INTO tableVALUES,,……),values的個數不應過多。
-
避免使用存儲過程、觸發器、函數等,容易將業務邏輯和DB耦合在一起,並且MySQL的存儲過程、觸發器、函數中存在一定的bug。
-
避免使用JOIN。
-
不使用ORDER BY RAND,使用其他方法替換。
-
建議使用合理的分頁方式以提高分頁的效率。
-
統計表中記錄數時使用COUNT(*),而不是COUNT(primary_key)和COUNT(1)。
-
禁止在從庫上執行后台管理和統計類型功能的QUERY。
-
散表
-
每張表數據量建議控制在5000w以下。
-
可以結合使用hash、range、lookup table進行散表。
-
推薦使用CRC32求余(或者類似的算術算法)進行散表,表名后綴使用數字,數字必須從0開始並等寬,比如散100張表,后綴從00-99。
-
使用時間散表,表名后綴必須使用特定格式,比如按日散表user_20110209、按月散表user_201102。