MySQL支持的數據類型非常多,選擇正確的數據類型對于獲得高性能至關重要。不管存儲哪種類型的數據,下面幾個簡單原則都有助于做出更好的選擇。
更小的通常更好
一般情況下,應該盡量使用可以正確存儲數據的最小數據類型。更小的數據類型通常更快,因為它們占用更少的磁盤、內存和CPU緩存,并且處理時需要的CPU周期也更少。
簡單就好
簡單數據類型的操作通常需要更少的CPU周期。例如,整型比字符串操作代價更低,因為字符集和校對規則(排序規則)使字符串比較比整型更復雜。
盡量避免NULL
很多表都包含可為NULL(空值)的列,即使應用程序并不需要保存NULL也是如此,這是因為可為NULL是列的默認屬性。通常情況下,最好知道列為NOT NULL,除非真的需要存儲NULL值。
如果查詢中包含可為NULL的列,對于MySQL來說更難優化,因為可為NULL的列使得索引、索引統計和值比較都更復雜。可為NULL的列會使用更多的存儲空間,在MySQL里也需要特殊處理。當可為NULL 的列被索引時,每個索引記錄需要一個額外的自己,在MyISAM里深圳還可能到固定大小的索引(例如只有一個整數列的索引)變成可變大小的索引。
通常把可為NULL的列改為 NOT NULL帶來的性能提升比較小,所以(調優時)沒有必要首先在現有schema中查找并修改掉這種情況,除非確定這會導致問題。但是,如果計劃在列上建索引,就應該盡量避免設計成可為NULL的列。
整數類型
對于整數類型,可以使用 TINYINT、 SMALLINT、 MEDIUMINT、 INT、 BIGINT 等。每個整數類型都對應著不同的存儲空間。
數據類型 | 存儲(Byte) |
---|---|
TINYINT | 1 |
SMALLINT | 2 |
MEDIUMINT | 3 |
INT | 4 |
BIGINT | 8 |
整數類型可以選擇 UNSIGNED 屬性,表示不允許負值,這樣可以使得正數的上限提高一倍。舉個例子,TINYINT 的存儲范圍是 -2-7 ~ 27 - 1,也就是 -128 ~ 127,那么 UNSIGNED INT 可以存儲的范圍就是 0 ~ 28 - 1,即 0 ~ 255。
MySQL可以為整數類型指定寬度,然而對大多數場景是沒有意義的:它并不會限制整數類型的合法范圍,它只是規定某些交互工具顯示出來的字符個數。如果不顯示地指定寬度,則默認為 INT(11)。有讀者會誤認為 INT(11) 指定整數類型的長度是 11 位,這個想法是錯誤的。實際上,在 Zerofill 屬性中,表示當數組寬度小于 11 位時,在數字前面加 0 填滿寬度。
實數類型
對于實數類型,可以使用 FLOAT、 DOUBLE、 DECIMAL 等。每個實數類型都對應著不同的存儲空間。
數據類型 | 存儲(Byte) |
---|---|
FLOAT | 4 |
DOUBLE | 8 |
FLOAT(M,D) 和 DOUBLE(M,D) 表示一共顯示 M 位整數,D 位小數。
舉個例子,FLOAT(5,2) 可以顯示為 100.99。此外,讀者還要注意的是,MySQL 保存時會進行四舍五入,因此,如果值為 100.0099, 會保存近似結果 100.01。
FLOAT 只保證 6 位有效數字的準確性,所以 FLOAT(M,D) 中,M<=6 時,數字通常是準確的。
DOUBLE 只保證 16 位有效數字的準確性,所以 DOUBLE(M,D) 中,M<=16 時,數字通常是準確的。
在使用實數類型,要重點考慮精度問題。DOUBLE 是 MySQL 內部浮點計算的類型,它比 FLOAT 有更高的精度和更大的范圍,但是 FLOAT 和 DOUBLE 都是不精確的,如果要實現精確浮點運算,就需要使用 DECIMAL 類型(例如,存儲財務數據)。
但在數據量比較大的時候,可以考慮使用BIGINT 代替DECIMAL ,將需要存儲的貨幣單位根據小數的位數乘以相應的倍數即可。假設要存儲財務數據精確到萬分之一,則可以把所有金額乘以一萬,然后將存儲結果存儲到BIGINT 里,這樣可以同時避免浮點存儲技術不精確和DECIMAL 精確計算代價高的問題。
字符串類型
MySQL支持多種字符串類型,可以使用 CHAR、 VARCHAR、 BLOB、 TEXT 等。
CHAR 類型是定長的。MySQL 會根據定義的長度分配空間。CHAR 長度可以是 0 到 255之間的值。
VARCHAR 類型用于存儲可變長字符串,它更加節省空間。值得注意的是, VARCHAR 需要使用 1 或 2 個額外字節記錄字符串的長度:如果列的最大長度小于或者等于255,則只使用1個字節表示,否則使用2個字節。VARCHAR 長度可以指定 0 到 65535 之間的值。
BLOB 和 TEXT 主要用來存儲大文本,分別采用二進制和字符串方式存儲。
實際上,它們分別屬于兩組不同的數據類型加載:字符串類型是 TINYTEXT、 SMALLTEXT、TEXT、MEDIUMTEXT、 LONGTEXT。
對應二進制類型是: TINYBLOB、SMALLBLOB、 BLOB 、MEDIUMBLOB、 LONGBLOB。
時間和時間類型
MySQL可以使用許多類型來保存日期和時間值,例如: YEAR、 DATE、 TIME、、 TIMESTAMP、DATETIME。
MySQL 能夠存儲的最小單位是秒,如果需要更精確的存儲,就必須自己定義存儲格式。比如可以使用BIGINT存儲毫秒級別的時間戳。
DATETIME類型范圍:'101-01-01 00:00:00' ~ '9999-12-31 23:59:59'。
TIMESTAMP類型范圍:'1970-01-01 00:00:01'UTC ~ '2038-01-19 03:14:07' UTC
DATETIME 和 TIMESTAMP 都可以存儲相同類型的數據,而 TIMESTAMP 只使用 DATETIME 一半的存儲空間。通常情況下,建議優先考慮 TIMESTAMP,因為它的空間利用率更高。
MySQL schema設計
范式和反范式
對于任意給定的數據通常都有很多種表示方式,從完全的范式化到完全的反范式化,以及兩者的折中。在范式化的數據庫中,每個數據會出現并且僅出現異常。相反,在反范式化的數據庫中,信息是冗余的,可能會存儲在多個地方。
范式的優點和缺點
范式化設計schema的優點:
范式化的更新操作通常比反范式化要快
當數據比較好地范式化時,就只有很少或者沒有重復數據,所以只需要修改更少的數據。
范式化的表通常更小,可以更好地放在內存里,所以執行操作會更快。
范式化設計schema的缺點是 通常需要關聯。稍微復雜一些的查詢語句在符合范式化的
schema上都可能需要至少一次關聯,也許更多。
事實上, 完全的范式化和完全的反范式化 都是實驗室里才有的東西,在真實世界中很少會這么極端的去使用。在實際應用中 經常需要混用 范式化和反范式化。