高性能MySQL - 數據類型優化

數據類型選擇原則

1. 小

儘可能使用可以正確存儲數據的最小數據類型,佔用更小的磁盤,內存和CPU緩存,處理週期也小。

2. 簡單

整型比字符操作代價低,字符集的較對規則使字符比整型比較更復雜,所以應該使用MySQL內建的類型而不是字符串來存儲日期和時間,另外應該用整型存儲IP地址,而不是字符串。

3.儘量避免NULL

NULL對於MySQL來說很難優化,NULL使得索引,索引統計和值比較都更爲複雜,而且會使用更多的存儲空間。

基本數據類型

MySQL爲了兼容性支持很多別名,例如INTEGER,BOOL,以及NUMERIC,但它們只是別名,如果用SHOW CREATE TABLE檢查,你會發現MySQL報告的是基本類型,而不是別名。

一、整型

TINYINT SMALLINT MEDIUMINT INT BIGINT
8 16 24 32 64

存儲範圍: -2^(N-1)到2^(N-1)-1,其中N是存儲空間的位數。
UNSIGN屬性,可以使得上限提高一倍,
-128 - 127 ——> 0 - 255
MySQL 可以爲整數指定寬度,例如INT(11),對大多數應用這是沒有意義的,它不會限制值的合法範圍,只是規定了MySQL一些交互工具用來顯示字符的個數,對於存儲和計算來說,INT(1)和INT(20)是相同的。

二、實數類型

實數是帶有小數部分的數字。然而,它們不只是爲了存儲小數部分;也可以使用DECIMAL存儲比BIGINT大的整數,MySQL既支持精確類型,也支持不精確類型。

FLOAT 和 DOUBLE支持使用標準的浮點運算進行相近似計算,DECIMAL類型用於存儲精確的小數,5.0 之後的版本支持精確計算。CPU不支持對它的直接計算,所以在MySQL5.0 之後的版本,MySQL服務器自身實現了DECIMAL的高精度計算,但相對於高精度的這個運算,浮點運算明顯會快一些。

浮點和DECIMAL類型都可以指定精度。對於DECIMAL 列,可以指定小數點前後所允許的最大位數。例如: DECIMAL(18,9)小數點兩邊各存儲9個數字,一共使用9個字節;小數點本身佔1個字節。

DECIMAL類型最多65個數字

因爲需要額外的空間和計算開銷,所以應該儘量只在對小數進行精度計算時才使用DECIMAL。

三、字符串類型

VARCHAR和CHAR是兩種最主要的字符串類型,具體怎麼存儲在磁盤和內存中的,跟存儲引擎的具體實現有關。下面的描述假設使用的是InnoDB和MyISAM。

VARCHAR
VARCHAR類型用於存儲可變長字符串,比定長類型更節省空間。所以他需要使用1或2個額外字節記錄字符串的長度:如果列的最大長度小於或等於255字節,則使用1個字節表示,否則2字節。假設採用latin1字符集,一個VARCHAR(10)的列需要11個字節的存儲空間。VARCHAR(1000)需要1002個字節。

Update的時候可能使行變得比原來更長,如果頁內沒有更多的空間,不同的存儲引擎會採取不同的處理方式。MyISAM會將行拆成不同的片段存儲,InnoDB則需要分裂頁來使行可以放進頁內。

使用VARCHAR(5)和VARCHAR(200)存儲‘hello’的空間開銷是一樣的,但是更長的列會消耗更多的內存,因爲MySQL通常會分配固定大小的內存塊來保存內部值。尤其是使用內存臨時表進行排序或操作時會特別糟糕。所以最好還是隻分配真正需要的空間。

下面情況下用VARCHAR是合適的:字符串列的最大長度比平均長度大很多;列的更新很少,所以碎片不是問題;使用了UTF-8這樣複雜的字符集,每個字符都使用不同的字節數進行存儲。

InnoDB 可以把過長的VARCHAR存儲爲BLOB。MySQL在存儲和檢索的時候會保留末尾空格,在4.1之後的版本。

CHAR
· CHAR類型是定長的。MySQL會刪除所有的末尾空格。CHAR適合存儲很短的字符串,或者所有值都接近同一個長度,例如,CHAR非常適合存放密碼的MD5值。

· 並且對於經常變更的數據,CHAR也比VARCHAR好,不容易產生碎片。

· 對於非常短的列,CHAR比VAERCHAR在存儲空間上也更有效率。例如CHAR(1)來存儲只有Y和N的值,只需要一個字節,VARCHAR要2個字節。

BINARY和VARBINARY
當需要存儲二進制數據,並且希望MySQL使用字節碼而不是字符進行比較時使用。二進制比字符比較簡單很多,所以也很快。

BLOB和TEXT
都是爲了存儲很大的數據而設計的字符串數據類型,分別採用二進制和字符方式存儲。

兩組不同的數據類型家族:

TINYTEXT SMALLTEXT TEXT MEDIUMTEXT LONGTEXT
TINYBLOB SMALLBLOB BLOB MEDIUMBLOB LONGBLOB

當BLOB和TEXT值太大時,InnoDB會使用專門的“外部”存儲區間進行存儲,此時每個值在行內需要1-4個字節存儲一個指針,然後在外部存儲區域存儲實際的值。

枚舉ENUM
有時候可以使用枚舉列代替常用的字符串類型。MySQL在內部會將每個值在列表中的位置保存爲整數,並且在表的.frm文件中保存“數字 - 字符串”映射關係的“查找表”

舉例:我們運行下面兩個語句:

CREATE TABLE enum_test(
    e ENUM('fish','apple','dog') NOT NULL
);
INSERT INTO enum_test(e) VALUES('fish'),('dog'),('apple');

這三行數據實際存儲爲整數,而不是字符串。可以通過在數字上下文環境檢索看到雙重屬性。
這裏寫圖片描述
另外枚舉字段是按照內部存儲的整數而不是定義的字符串進行排序的。
枚舉最不好的地方,字符串列表是固定的,添加和刪除字符串必須使用ALTER TABLE,因此,對於一系列未來可能會改變的字符串,使用枚舉不是一個好主意,除非能接受在列表末尾添加元素。

日期和時間類型
DataTime
這個類型能保存大範圍的值,從1001年到9999年,精確爲秒。它把日期和時間封裝到格式YYYYMMDDHHMMSS的整數中,與時區無關,使用8個字節的空間。
TIMESTAMP
從1970年1月1日午夜以來的秒數,它和UNIX時間戳相同。只使用4個字節的存儲空間,範圍比DATETIME小,只能表示從1970到2038年。MySQL提供FROM_UNIXTIME()函數把UNIX時間戳轉換成日期,UNIX_TIMESTAMP()轉換回去。跟時區有關。

除了特殊行爲之外,通常應該儘量使用TIMESTAMP,因爲它空間效率更高。

以上兩種都只保存到秒,之前在七牛碰到過這個問題,要想保存更小的,只能使用自己的存儲格式,比如可以使用BIGINT類型存儲微妙級別的時間戳。

位數據類型
BIT
可以使用BIT列在一列中存儲一個或多個true/false值。BIT(2)存儲2位。最大長度64. MySQL把BIT當作字符串使用,所以不要隨意加減運算,不然會出現問題。
SET
如果需要保存很多true/false值,可以考慮合併這些列到一個SET,主要的缺點是改變列的定義的代價較高。

IPV4地址,實際上是32位無符號整數,不是字符串。所以應該用無符號整數存儲IP地址,MySQL提供INET_ATON() 和INET_NTOA()函數在這兩種方法之間轉換。

發佈了54 篇原創文章 · 獲贊 32 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章