來(lái)自公眾號(hào):譚小譚
先說(shuō)結(jié)論,mysql 中的 varchar 是有最大長(zhǎng)度限制的,這個(gè)值是 65535 個(gè)字節(jié)。
varchar(100),這個(gè) 100 的單位是啥,這個(gè)單位其實(shí)在不同版本中是不一樣的。
在早期低版本中代表的是字節(jié),具體哪個(gè)版本我也沒(méi)去驗(yàn)證了,后來(lái)被改成了字符,不過(guò)可以肯定的是在 5.1 版本后, varchar(100) 就是指 100 個(gè)字符。
說(shuō)到 varchar ,一般都會(huì)拿 char 來(lái)做比較說(shuō)明。
char 是固定長(zhǎng)度,其單位也是字符,比如 char(10),就表示不管你給的什么值,都會(huì)被 mysql 固定保存成 10 個(gè)字符。
如果給的字符長(zhǎng)度小于 10,那么在尾部就會(huì)自動(dòng)用空格補(bǔ)齊。
如果大于 10,在嚴(yán)格模式(strict sql mode)下就會(huì)報(bào)錯(cuò),在非嚴(yán)格模式下就會(huì)對(duì)內(nèi)容做自動(dòng)截取操作。
另外 char 也是有最大長(zhǎng)度限制的,最大長(zhǎng)度為 255, 即 char 類(lèi)型最多只能保存 255 個(gè)字符,char(256) 這都是錯(cuò)誤的寫(xiě)法,可以看下面的例子。
創(chuàng)建一個(gè)表,同時(shí)聲明address字段長(zhǎng)度為256會(huì)報(bào)錯(cuò),最大為255
mysql> create table test2 ( address char(256) );
ERROR 1074 (42000): Column length too big for column 'address' (max = 255); use BLOB or TEXT instead
下面是 char 和 varchar 對(duì)于同一個(gè)字符所需要的不同的存儲(chǔ)空間,這里假設(shè)使用的是 latin1 單字節(jié)字符集。
值 CHAR(4) 存儲(chǔ)空間大?。ㄗ止?jié)) VARCHAR(4) 存儲(chǔ)空間大小(字節(jié))
'' ' ' 4 bytes '' 1 byte
'ab' 'ab ' 4 bytes 'ab' 3 bytes
'abcd' 'abcd' 4 bytes 'abcd' 5 bytes
'abcdef' 'abcd' 4 bytes 'abcd' 5 bytes
細(xì)心的你可能會(huì)發(fā)現(xiàn) varchar 的存儲(chǔ)空間會(huì)比字符的實(shí)際長(zhǎng)度多 1 個(gè)字節(jié),這是因?yàn)?varchar 需要額外增加 1 到 2 個(gè)字節(jié)來(lái)存儲(chǔ)字符的長(zhǎng)度,這個(gè)值被稱作前綴。
也就是說(shuō)在 varchar 類(lèi)型中,除了字符本身實(shí)際占用的空間外,還需要 1 個(gè)或 2 個(gè)字節(jié)來(lái)聲明這個(gè)字符的長(zhǎng)度。
如果存儲(chǔ)的值小于 255 個(gè)字節(jié),則使用 1 個(gè)字節(jié)來(lái)存儲(chǔ)前綴,如果大于 255 個(gè)字節(jié)則使用 2 個(gè)字節(jié)來(lái)存儲(chǔ)前綴。
關(guān)于字符集和字節(jié)的關(guān)系,以及字節(jié) (byte) 與位 (bit) 的關(guān)系。
1 byte (字節(jié)) = 8 bit (位)
2^8 = 256
所以計(jì)算機(jī)里常見(jiàn)的 255、256 臨界值絕大多數(shù)與這個(gè)有關(guān)
1 個(gè)字節(jié)具體占多大的空間,這與所使用的字符集有關(guān)系
比如 latin1 單字節(jié)字符集,1 個(gè)字符即占 1 個(gè)字節(jié)
我們常見(jiàn)的 GBK、UTF8、UTF8-MB4 這些都是多字節(jié)字符集
GBK :一個(gè)字符最多占 2 個(gè)字節(jié)
UTF8:一個(gè)字符最多占 3 個(gè)字節(jié)
UTF8MB4:一個(gè)字符最多占 4 個(gè)字節(jié)
好了,再堅(jiān)持一會(huì),回到文章開(kāi)頭的問(wèn)題,為啥 varchar 最大長(zhǎng)度是 65535 個(gè)字節(jié)呢,其實(shí)這個(gè)是受 mysql 另一個(gè)規(guī)則限制導(dǎo)致的,mysql 規(guī)定了每行數(shù)據(jù)大小不能超過(guò) 65535 個(gè)字節(jié)。
另外還有一個(gè)小問(wèn)題,一個(gè)字段如果允許為 null ,在 mysql 中也是需要增加額外空間來(lái)單獨(dú)標(biāo)識(shí)的,反之則不需要這個(gè)額外空間,至于這個(gè)空間大小具體是怎么計(jì)算的,我目前也還沒(méi)有研究過(guò)。
結(jié)合上面說(shuō)的幾點(diǎn),下面來(lái)通過(guò)幾個(gè)實(shí)例來(lái)驗(yàn)證下。
字符集設(shè)置為latin1,1個(gè)字符=1個(gè)字節(jié),字段允許為null
mysql> create table test ( address varchar(65536) default null ) charset=latin1;
ERROR 1074 (42000): Column length too big for column 'address' (max = 65535); use BLOB or TEXT instead
減去varchar前綴長(zhǎng)度標(biāo)識(shí)2個(gè)字節(jié),還是報(bào)錯(cuò),說(shuō)明null標(biāo)識(shí)也占用了空間
mysql> create table test ( address varchar(65533) default null ) charset=latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
把字段設(shè)置為非空即可建表成功
mysql> create table test ( address varchar(65533) not null ) charset=latin1;
Query OK, 0 rows affected (0.01 sec)
如果字符集設(shè)置為UTF8,那么 max=65535/3=21845,一個(gè)字符最多占3個(gè)字節(jié)
mysql> create table test2 ( address varchar(65533) not null ) charset=UTF8;
ERROR 1074 (42000): Column length too big for column 'address' (max = 21845); use BLOB or TEXT instead
基于上面幾個(gè)實(shí)例,基本上可以得出計(jì)算 varchar 最大長(zhǎng)度限制的公式。
varchar 最大長(zhǎng)度限制 = (行最大字節(jié)數(shù)(65535) - null 標(biāo)識(shí)字節(jié)數(shù) - 長(zhǎng)度前綴字節(jié)數(shù)(1或2)) / 字符集單字符占用最多字節(jié)數(shù)
看到這里,不知道你有沒(méi)有一個(gè)疑問(wèn),為什么長(zhǎng)度前綴 1 或 2 個(gè)字節(jié)就夠用了呢,因?yàn)?2 個(gè)字節(jié)的話,2^16 = 65536,這已經(jīng)超過(guò) mysql 行最大字節(jié)數(shù) 65535 的限制了,所以 1 到 2 個(gè)字節(jié)就夠用了。
最后再看一個(gè)綜合例子,我們創(chuàng)建一個(gè)表,采用 UTF8 字符集,添加兩個(gè)非空字段,分別為 char 和 varchar 類(lèi)型,char 類(lèi)型長(zhǎng)度給定為 255。
那么 varchar 類(lèi)型字段的最大字節(jié)數(shù)應(yīng)該就是,
65535 (行最大字節(jié)數(shù)) - 255*3 (一個(gè)字符最多占 3 個(gè)字節(jié)) = 64770,
然后再減去 2 個(gè)長(zhǎng)度前綴字節(jié),
64770 - 2 = 64768,
最后再算出 varchar 最大字符數(shù)為 64768 / 3 = 21589.33,
字符數(shù)21590超過(guò)最大字符數(shù)會(huì)報(bào)錯(cuò)
mysql> create table test4 (name char(255) not null, address varchar(21590) not null ) charset=utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
字符數(shù)21589則剛好能創(chuàng)建成功
mysql> create table test4 (name char(255) not null, address varchar(21589) not null ) charset=utf8;
Query OK, 0 rows affected, 1 warning (0.02 sec)
好了,關(guān)于 varchar 的最大長(zhǎng)度問(wèn)題就聊到這了,文中如有錯(cuò)誤,歡迎大家批評(píng)指出,更歡迎大家交流討論,如果文章對(duì)你有幫助,點(diǎn)個(gè)在看表示對(duì)我的支持哈,感謝。