
良好的邏輯設計和物理設計事高性能的基石,在進行數(shù)據(jù)庫設計時,我們應該要考慮到未來將會執(zhí)行的查詢語句,這就需要對各種因素進行權(quán)衡。本文將會聊一聊數(shù)據(jù)庫(MySQL)設計中有關(guān)數(shù)據(jù)類型優(yōu)化的一些內(nèi)容。以下內(nèi)容總結(jié)自《高性能 MysQL》。
選擇優(yōu)化的數(shù)據(jù)類型
原則
我們知道 MySQL 支持多種數(shù)據(jù)類型,通常情況下,很多數(shù)據(jù)類型都可以完成相同的工作,選擇正確的數(shù)據(jù)類型對于高性能至關(guān)重要。在明確需要優(yōu)化的數(shù)據(jù)類型前,我們需要先掌握幾個原則,這些原則有助于我們作出更好的選擇。
- 選擇相對輕量的數(shù)據(jù)類型
這里的輕量指的是,在一般情況下,應該使用可以正確存儲數(shù)據(jù)的最小數(shù)據(jù)類型。例如一個列中的最大值為 2020,那就沒必要選擇 INT 以及更大的數(shù)據(jù)類型。
- 簡單就好
簡單的數(shù)據(jù)類型通常意味著處理時需要更少的 CPU 周期。例如,整型比字符操作代價更低;存儲日期/時間應該用內(nèi)置的數(shù)據(jù)類型而不是字符串;存儲 IP 地址時應該用整型而不是字符串。
- 避免 NULL
通常情況下,應該設置列為 NOT NULL。因為 NULL 會使某個列的索引、值等數(shù)據(jù)的處理變得復雜。而且可為 NULL 的列會使用更多的存儲空間。
針對具體的數(shù)據(jù)類型
整數(shù)類型
對于整數(shù),可選的數(shù)據(jù)類型有 TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT。分別使用 8、16,24,32,64 位存儲空間。同時可選的屬性有UNSIGNED,表示不允許有負值,這可以讓正數(shù)的上限提高一倍。例如 TINYINT 的存儲范圍是-128~127,TINYINT UNSIGNED 的存儲范圍是0~255。
MySQL 可以為整數(shù)類型制定寬度,例如 INT(11),但是并不會限制數(shù)值的合法范圍,只會控制某些交互工具用來顯示字符的個數(shù),對于存儲和計算來說,INT(1)和 INT(20)是相同的。即括號中的數(shù)字只是用于控制顯示的字符數(shù),和實際可以存儲的字符數(shù)無關(guān)。
實數(shù)類型
實數(shù)是帶有小數(shù)部分的數(shù)字,MySQL 中使用 DECIMAL 類型用于存儲精確的小數(shù),但是 CPU 不支持對 DECIMAL 的直接計算,因此 MySQL 服務器自身實現(xiàn)了 DECIMAL 的高精度計算。
與此同時 CPU 支持原生浮點計算,因此浮點數(shù)的運算速度相對 DECIMAL 會更快。浮點分為兩種:FLOAT 和 DOUBLE。由于 DOUBLE 相對于 FLOAT 有更高的精度和更大的范圍,MySQL 使用 DOUBLE 作為內(nèi)部浮點計算的類型。
因為需要額外的空間和計算開銷,所以應該盡量只在對小數(shù)進行精確計算時才使用 DECIMAL,例如財務數(shù)據(jù)。數(shù)據(jù)量較大時,也可以考慮使用 BIGINT 代替 DECIMAL,將需要存儲的貨幣單位根據(jù)小數(shù)的位數(shù)乘以相應的倍數(shù)即可。
字符串類型
常用的是 VARCHAR 和 CHAR
VARCHAR
存儲可變長字符串
比定長類型省空間,越短的字符串使用空間越少
使用 1 或 2 個額外字節(jié)記錄字符串的長度。列的最大長度小于或等于 255 字節(jié),使用 1 個字節(jié)表示,否則使用 2 個字節(jié)表示
CHAR
存儲定長字符串
對于經(jīng)常變更的數(shù)據(jù),使用 CHAR 存儲不易產(chǎn)生碎片
因此二者的使用場景也很明顯:
VARCHAR 適合字符串列的最大長度比平均長度大很多,同時列的更新很少,以及列中的字符串使用復雜的字符集,每個字符都使用不同的字節(jié)數(shù)進行存儲(UTF-8)。
CHAR 適合存儲很短的字符串,或者所有值都接近同一個長度,例如存儲密碼的 MD5 值,或者用 CHAR(1)存儲只有 Y 和 N 的值,因為 CHAR(1)需要一個字節(jié),VARCHAR(1)需要兩個字節(jié)(需要一個記錄長度的額外字節(jié))。
日期和時間
DATETIME
可以保存從 1001 年到 9999 年,精度為秒
將日期和時間封裝到格式為 YYYYMMDDHHMMSS 的整數(shù)中,與時區(qū)無關(guān)。
使用 8 個字節(jié)的存儲空間
TIMESTAMP
保存了從 1970 年 1 月 1 日午夜以來的秒數(shù)
只使用 4 個字節(jié)存儲,因此范圍會小很多(最多表示從 1970 年到 2038 年)
和時區(qū)有關(guān)
因為 TIMESTAMP 是用 4 個字節(jié)存儲,因此最多只能保存到 2038 年,這一點也造成了非常著名的2038 年問題。
選擇標識符
標識符是用于標識列與其他值進行比較(例如關(guān)聯(lián)操作中,通過標識列尋找其他列),標識列在選擇數(shù)據(jù)類型時,應該跟關(guān)聯(lián)表中的對應列一樣的類型。
整數(shù)類型通常是最好的選擇,效率高且可以自增長(例如主鍵)。如果可以,盡量避免使用字符串作為標識列,消耗空間,且查詢速度慢。
特殊數(shù)據(jù)
有些特殊的數(shù)據(jù)需要用一些數(shù)據(jù)類型專門存儲,例如存儲 IP 地址應該用無符號整數(shù),因為 IP 地址本質(zhì)上是 32 位無符號數(shù),并不是字符串,用小數(shù)點將地址分成四段只是方便閱讀。
總結(jié)
想要提高 MySQL 的效率,可以做的功課非常多,數(shù)據(jù)庫的數(shù)據(jù)類型優(yōu)化也只是其中很小的一點,本文也只是挑出了常用的數(shù)據(jù)類型進行介紹。有興趣的可以仔細閱讀《高性能 MySQL》這本書,你可以在微信公眾號「01 二進制」后臺回復「高性能 MySQL」獲取本書。
大道至簡,盡可能將事情保持簡單總是好的,MySQL 喜歡簡單,希望使用數(shù)據(jù)庫的你也會喜歡簡單。