在MySQL中者填,char和varchar可能是我們最常使用字符串類型。那么到底varchar和varchar有什么不同穴亏?我們什么時候使用char重挑,什么時候使用varchar呢谬哀?
區(qū)別
char
char是定長的,插入數(shù)據(jù)不足規(guī)定長度的谦屑,右邊補空格,當然查詢出來的數(shù)據(jù)也會有空格酝枢,插入數(shù)據(jù)超過規(guī)定長度悍手,會返回錯誤[22001][1406] Data truncation: Data too long for column 'name' at row 1
坦康,MySQL
并不會自動截短字符串。因為char是定長的古胆,所以查詢的效率比varchar高(后面會將為什么效率高)筛璧,但在列容量不能充分利用的情況下會造成一定的空間浪費。
varchar
varchar是不定長的桶良,varchar類型的列是不定長的沮翔,在5.0版本以后的最大長度是65536字節(jié)(2^16),但是這個長度只是“系統(tǒng)長度”,這并不意味著你真的可以完全利用65536字節(jié)來存儲數(shù)據(jù)疲牵,因為varchar是不定長的榆鼠,所以需要前兩個字節(jié)標記字段的實際長度妆够,結(jié)尾還要用一個字節(jié)表示結(jié)束。
需要注意的是65535只是字節(jié)個數(shù)颓哮,而且是理論字節(jié)個數(shù)鸵荠,在減去頭尾的"系統(tǒng)"占用字節(jié)后,只剩下65533可用字節(jié)姨伤。那么我們建表的時候哨坪,能不能直接寫varchar(65533)呢?當然是不可以的乍楚,因為4.0之后当编,varchar后面的小括號里就不再是字節(jié)長度了,而是字符長度徒溪。
字節(jié)和字符個數(shù)之間的換算關(guān)系是根據(jù)編碼決定的:
編碼 | 長度 |
---|---|
utf8 | 65533/3=21844(漢字占3個字符) |
utf8mb4 | 65533/4=16383(漢字占4個字符凌箕,包含了生僻漢字和文字表情) |
我們只列出了常用的編碼格式。
那么這是否意味著词渤,在utf8mb4編碼下我們可以用varchar(16383)來定義一個列呢?
答案是要看情況串绩,MySQL規(guī)定了一個row
所有的字段加起來總長度不能超過65535字節(jié)缺虐,所以如果一個表只有一個列礁凡,那完全可以用varchar(16383)來定義這個列高氮,如果這個表還有其他列,無論其他列多么短顷牌,都是會占用字節(jié)數(shù)的剪芍,所以,使用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
類型状共。
所以,如果我們要創(chuàng)建一個只包含兩個字段的表(編碼是utf8mb4)谁帕,一列是主鍵峡继,一列是字符串,字符串的最大長度是多少呢匈挖?你可以先自己算一下碾牌,再往下看。
列 | 長度 |
---|---|
id | int(11) |
article | varchar((65535-4)/4=16382) |
為什么65535要減去4呢儡循?因為int(11)占4個字節(jié)舶吗,那么在utf8編碼情況下,還是同樣的數(shù)據(jù)結(jié)構(gòu)贮折,article的最大長度有事多少呢裤翩?
列 | 長度 |
---|---|
id | int(11) |
article | varchar((65535-4)/3=21843) |
相信這次你一定算對了。
為什么char類型查詢效率高
這是由他們在磁盤上存放的不同形式?jīng)Q定的,我們先來看一個圖:
我們可以看到char類型在存放數(shù)據(jù)的時候踊赠,中間是沒有間隔的呵扛,數(shù)據(jù)本身是有空格的,但是數(shù)據(jù)段之間沒有間隔筐带,因為我們在創(chuàng)建列的時候已經(jīng)告訴
MySQL
列的長度了今穿,MySQL
在查詢數(shù)據(jù)的時候,只需要按部就班尋找就行了伦籍,不需要在中途計算這個數(shù)據(jù)段的長度蓝晒。
但是varchar類型的存放就不同了,在每個數(shù)據(jù)段開頭帖鸦,都要有一段空間(1~2個字節(jié))存放數(shù)據(jù)段的長度芝薇,在數(shù)據(jù)段的結(jié)尾還有一段空間(1個字節(jié))標記此字段的節(jié)數(shù)。MySQL
在讀取一個數(shù)據(jù)段的時候作儿,首先要讀開頭洛二,比如讀到了3,說明數(shù)據(jù)段的長度是3攻锰,之后就不多不少晾嘶,只讀3個字節(jié)。所以MySQL
在遍歷數(shù)據(jù)的時候娶吞,磁針要比char類型的列多讀很多次磁盤來獲取字段的真實長度垒迂,這就是為什么varchar比char查詢效率低的原因了。
應(yīng)用
我們可以用varchar存放不定長的數(shù)據(jù)妒蛇,比如人的名字机断,或者一篇博客的文章⌒宥幔可以用char存放定長的數(shù)據(jù)毫缆,比如身份證號和手機號,我們把一個列定義為mobile varchar(11)
乐导,中國大陸的手機號最長苦丁,達到11位,香港是8位物臂,瑞士是10位旺拉,所以定義成11位完全夠用,可以存放各國的手機號了棵磷。
附加
除了char和varchar類型蛾狗,最常用的就是數(shù)值類型了,為了方便建表的時候計算列的最大長度仪媒,把數(shù)值類型占用的字節(jié)和值的范圍放在這里: