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類型,最常用的就是數值類型了,為了方便建表的時候計算列的最大長度,把數值類型占用的字節和值的范圍放在這里:
