mysql進階系列:表設計如何更好的選擇資料型別
日常工作中我們會接觸到不同業務,同樣也會設計不同的表,但是你有真正考慮的mysql支援的那些資料型別嗎?知道如何根據不同的需求選擇最合適或者正確的型別嗎。
儲存字串型別只知道選擇varchar , 是否知道字串還有char, text, blob 。
儲存數字型別只知道選擇 int,float,double,是否知道還有tinyint,smallint 等。
看完這篇文章相信你在以後的表設計中不會再糾結究竟該使用什麼欄位了。
優化選擇資料型別的簡單原則:
1. 更小的通常更好
更小的資料型別佔用更少的磁碟,記憶體和CPU快取,而且處理時需要的CPU週期也更少,所以會更快(但是要確保沒有低估需要儲存的值的範圍)
2. 簡單就好
簡單的資料型別的操作通常需要更少的CPU週期。
例如:
-
整型比字串操作代價更低,因為字符集和校對規則使 字串比較 比 整型比較 更復雜。
-
使用mysql自建型別而不是字串來儲存日期和時間
-
用int儲存IP地址比字串要節約空間(ip用select INET_ATON('192.1.1.101') 和 select INET_NTOA(131351321))
3. 儘量避免null
如果查詢中包含null的列使得索引,索引統計和值比較都更為複雜。所以如果在列上建索引,就應該儘量避免設計成可為null的列。
實際資料型別
1. 整數型別
可以使用的整數型別儲存空間(位)由小到大分別是tinyint(8)、smallint(16)、mediumint(24)、int(32)、bigint(64)。
如何選擇:儘可能使用滿足需求的最小資料型別。
例如: 性別只有男和女可能還有未知,如果用數字表示就是未知(1),男(0),女(2),那麼型別可以選擇tinyint,沒必要選擇其他的更大的儲存空間的型別。
小知識點1: 整數型別中還有個可選項UNSIGNED屬性,表示不允許有負值。這可以使得正整數的上限提高一倍。例如本來tinyint 可以儲存的範圍是-128~127,而tinyint UNSIGNED 可以儲存的範圍變成0~255(有無UNSIGNED 儲存空間都是一樣的,效能也不變)。
小知識點2: mysql中可以設定型別的寬度,例如int(11),實際上是沒有意義的,只是用來互動工具顯示字元的個數,實際上對於儲存和計算來說,int(1)和int(10) 是相同的。
2. 實數型別
實數是帶有小數部分的數字,而且不只是為了儲存小數部分,也可以使用DECIMAL儲存比BIGINT還大的整數。
Decimal 型別用於儲存精確的小數。(CPU支援原生浮點計算,不支援對decimal的直接計算,所以浮點運算更快)。
浮點型別在儲存同樣範圍的值時,通常比decimal使用更少的空間,float用4位元組儲存,double用8個位元組(mysql使用double作為內部浮點計算)
只有需要對小數進行精確計算時才使用decimal(例如財務資料),但是當資料量較大的時候,可以考慮使用bigint代替decimal,將需要儲存的貨幣單位根據小數的位數乘以相應的倍數即可。
-
假如要儲存財務資料精確到萬分之一,可以把所有金額乘以一百萬,
然後將結果儲存在bigint裡,可以避免浮點儲存計算不精確和decimal精確計算代價高的問題。
3. 字串型別
字串型別包含varchar(工作中首選),char,text,blob。
varchar:儲存的是可變長度字串,比定長型別更節省空間(越短字串使用越少的空間)。
-
使用最小的符合需求的長度。
-
varchar(n) n<=255的時候使用一個位元組來儲存長度,當n>255的時候需要兩個位元組儲存長度。
-
varchar(5)和varchar(200)儲存同樣的內容,硬碟的儲存空間是一樣的,不同的是記憶體的消耗。mysql通常會分配固定大小的記憶體塊來儲存內部值,尤其是使用記憶體臨時表進行排序或者操作的時候會很糟糕。
所以最好是隻分配真正需要的空間。
應用場景:
儲存波動長度較大的資料,如:文章,有的短有的長。
字串很少更新的場景,每次更新都會重新算並使用額外儲存空間儲存長度。
適合儲存多位元組字元,如漢字,特殊字元等。
char: 定長,最大長度255,
儲存char值時,mysql會刪除所有資料的末尾空格,
比較適合非常短的資料,例如char(1)來儲存Y和N的值。
應用場景:
-
儲存長度波動不大的資料,如:md5摘要(加密後的密碼)
-
儲存短字串,經常更新的字串。
BLOB和TEXT型別
BLOB和text是儲存很大的資料而設計的字串資料型別,分別採用二進位制和字元方式儲存。
不建議使用,資料量很大的時候會影響效率
4. 日期和時間
dateTime:8個位元組的儲存空間,可以儲存大範圍的值,儲存1000-01-01到9999-12-31之間的日期,精度為秒,將日期和時間封裝到格式為YYYYMMDDHHMMSS的整數中,與時區無關。不要使用字串儲存日期型別,佔用空間大,損失日期型別函式的便捷性。
timestamp:4個位元組的儲存空間,儲存了從1970年1月1日午夜(格林尼治標準時間)以來的秒數,和unix時間戳相同。timestamp的範圍只表示從1970年到2038年。timestamp的顯示的值依賴於時區。
date: 3個位元組的儲存空間,date型別還可以利用日期時間函式進行日期之間的計算。用於儲存1000-01-01到9999-12-31之間的日期。
如果想要儲存更小粒度的日期和時間值: 可以使用bigint 型別儲存微秒級別的時間戳,或者使用double儲存秒之後的小數部分
5. 列舉型別
使用列舉替代常用字串型別,沒有生氣儲存列舉型別會非常緊湊,會根據列表值的資料壓縮到一個或者兩個位元組中,mysql會將每個值在列表中的位置儲存為整數,並且在表的.frm檔案中儲存‘數字’,‘字串’的對映關係的查詢表。
如果直接在資料庫儲存列舉性別,0代表男,1代表女,查詢的時候可以直接看到男/女
mysql> create table enum_test( sex enum('男','女','未知') not null ) charset=utf8; Query OK, 0 rows affected (0.03 sec) mysql> INSERT INTO enum_test(sex) values('男'),('女'),('未知'),('男'); Query OK, 4 rows affected (0.01 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from enum_test; +--------+ | sex | +--------+ | 男 | | 女 | | 未知 | | 男 | +--------+ 4 rows in set (0.00 sec) -- 實際底層儲存的是整型值 mysql> select sex+0 from enum_test; +-------+ | sex+0 | +-------+ | 1 | | 2 | | 3 | | 1 | +-------+ 4 rows in set (0.00 sec)
型別是很重要的,如果表設計的比較好,在大資料量下優勢就體現出來了