高性能MySQL讀書筆記 - Schema與數據類型優化

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上都可能需要至少一次關聯,也許更多。

事實上, 完全的范式化和完全的反范式化 都是實驗室里才有的東西,在真實世界中很少會這么極端的去使用。在實際應用中 經常需要混用 范式化和反范式化。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容