char和varchar的區別
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
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型別,最常用的就是數值型別了,為了方便建表的時候計算列的最大長度,把數值型別佔用的位元組和值的範圍放在這裡:
MySQL數值型別的取值範圍和佔用位元組數.png