char
char是定長的,插入數據不足規定長度的,右邊補空格,當然查詢出來的數據也會有空格,插入數據超過規定長度,會返回錯誤[22001][1406] Data truncation: Data too long for column 'name' at row 1,MySQL並不會自動截短字符串。因為char是定長的,所以查詢的效率比varchar高(后面會將為什么效率高),但在列容量不能充分利用的情況下會造成一定的空間浪費。
varchar
varchar是不定長的,varchar類型的列是不定長的,在5.0版本以后的最大長度是65535字節(2^16),但是這個長度只是“系統長度”,這並不意味着你真的可以完全利用65535字節來存儲數據,因為varchar是不定長的,所以需要前兩個字節標記字段的實際長度,結尾還要用一個字節表示結束,這可以用u盤來說明,買到一個256G的u盤,用工具查看u盤的實際容量時,會發現不足256G,因為系統也要占用一部分。
需要注意的是65535只是字節個數,而且是理論字節個數,在減去頭尾的"系統"占用字節后,只剩下65532可用字節。那么我們建表的時候,能不能直接寫varchar(65532)呢?當然是不可以的,因為4.0之后,varchar后面的小括號里就不再是字節長度了,而是字符長度。
字節和字符個數之間的換算關系是根據編碼決定的:
| 編碼 | 長度 |
|---|---|
| utf8 | 65532/3=21844(漢字占3個字符) |
| utf8mb4 | 65532/4=16383(漢字占4個字符,包含了生僻漢字和文字表情) |
我們只列出了常用的編碼格式。
那么這是否意味着,在utf8mb4編碼下我們可以用varchar(16383)來定義一個列呢?
答案是要看情況,MySQL規定了一個row所有的字段加起來總長度不能超過65535字節,所以如果一個表只有一個列,那完全可以用varchar(16383)來定義這個列,如果這個表還有其他列,無論其他列多么短,都是會占用字節數的,所以,使用varchar(16383)來定義的時候,MySQL會返回錯誤提示:ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs,意思是row的容量太大,超出了row的最大容量65535,如果不改變列的長度的話,推薦使用TEXT or BLOBs類型。
所以,如果我們要創建一個只包含兩個字段的表(編碼是utf8mb4),一列是主鍵,一列是字符串,字符串的最大長度是多少呢?你可以先自己算一下,再往下看。
| 列 | 長度 |
|---|---|
| id | int(11) |
| article | varchar((65535-4)/4=16382) |
為什么65535要減去4呢?因為int(11)占4個字節,那么在utf8編碼情況下,還是同樣的數據結構,article的最大長度有事多少呢?
| 列 | 長度 |
|---|---|
| id | int(11) |
| article | varchar((65535-4)/3=21843) |
相信這次你一定算對了。
為什么char類型查詢效率高
這是由他們在磁盤上存放的不同形式決定的,我們先來看一個圖:
我們可以看到char類型在存放數據的時候,中間是沒有間隔的,數據本身是有空格的,但是數據段之間沒有間隔,因為我們在創建列的時候已經告訴
MySQL列的長度了,
MySQL在查詢數據的時候,只需要按部就班尋找就行了,不需要在中途計算這個數據段的長度。
但是varchar類型的存放就不同了,在每個數據段開頭,都要有一段空間(1~2個字節)存放數據段的長度,在數據段的結尾還有一段空間(1個字節)標記此字段的節數。MySQL在讀取一個數據段的時候,首先要讀開頭,比如讀到了3,說明數據段的長度是3,之后就不多不少,只讀3個字節。所以MySQL在遍歷數據的時候,磁針要比char類型的列多讀很多次磁盤來獲取字段的真實長度,這就是為什么varchar比char查詢效率低的原因了。
應用
我們可以用varchar存放不定長的數據,比如人的名字,或者一篇博客的文章。可以用char存放定長的數據,比如身份證號和手機號,我們把一個列定義為mobile char(11),中國大陸的手機號最長,達到11位,香港是8位,瑞士是10位,所以定義成11位完全夠用,可以存放各國的手機號了。
附加
除了char和varchar類型,最常用的就是數值類型了,為了方便建表的時候計算列的最大長度,把數值類型占用的字節和值的范圍放在這里:
