一. varchar存儲規則:
5.0版本以上,varchar(20),指的是20字符,無論存放的是數字、字母還是UTF8漢字(每個漢字3字節),都可以存放20個,最大大小是65532字節?
二. varchar和char 的區別:
char是一種固定長度的類型,varchar則是一種可變長度的類型,它們的區別是: char(M)類型的數據列里,每個值都占用M個字節,如果某個長度小于M,MySQL就會在它的右邊用空格字符補足.(在檢索操作中那些填補出來的空格字符將被去掉)在varchar(M)類型的數據列里,每個值只占用剛好夠用的字節再加上一個用來記錄其長度的字節(即總長度為L+1字節).?
在MySQL中用來判斷是否需要進行對據列類型轉換的規則
1、在一個數據表里,如果每一個數據列的長度都是固定的,那么每一個數據行的長度也將是固定的.
2、只要數據表里有一個數據列的長度的可變的,那么各數據行的長度都是可變的.
1、限制規則
字段的限制在字段定義的時候有以下規則:
a) 存儲限制
NULL標識位,如果varchar字段定義中帶有default null允許列空,則需要需要1bit來標識,每8個bits的標識組成一個字段。一張表中存在N個varchar字段,那么需要(N+7)/8 (取整)bytes存儲所有的NULL標識位。
mysql> create table t1 ( name varchar(65532) default null)charset=latin1; Query OK, 0 rows affected (0.09 sec)mysql>
mysql> create table t2 ( name 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. You have to change some columns to TEXT or BLOBs mysql>
可以看見當設置長度為65533時,已經超過行最大長度,我們可以計算一下,行最大長度是65535字節。上面t2表name字段使用varchar(65533),字符集是latin1,占用1個字節。還有默認為空,那么還有null標識位,( 1 + 7 ) / 8 =1,所以null標識位占用1個字節。現在我們來看看,65533 + 1 + 2=65536字節,已經大于行最大長度。這里2字節怎么來的???因為varchar類型存儲變長字段的字符類型,與char類型不同的是,其存儲時需要在前綴長度列表加上實際存儲的字符,當存儲的字符串長度小于255字節時,其需要1字節的空間,當大于255字節時,需要2字節的空間。
mysql> create table t2 ( name varchar(65533) not null) charset=latin1; Query OK, 0 rows affected (0.03 sec)mysql>
mysql> create table t3 ( name varchar(65534) not null) charset=latin1; 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 mysql>
字符類型若為gbk,每個字符最多占2個字節,最大長度不能超過32766;
字符類型若為utf8,每個字符最多占3個字節,最大長度不能超過21845。
c) 行長度限制
導致實際應用中varchar長度限制的是一個行定義的長度。?MySQL要求一個行的定義長度不能超過65535。若定義的表長度超過這個值,則提示
2、計算例子
舉兩個例說明一下實際長度的計算。
a) ?若一個表只有一個varchar類型,如定義為
create table t4(c varchar(N)) charset=gbk;
則此處N的最大值為(65535-1-2)/2=?32766。
減2的原因是varchar頭部的2個字節表示長度;
b) 若一個表定義為
create table t4(c int, c2 char(30), c3 varchar(N)) charset=utf8;
則此處N的最大值為?(65535-1-2-4-30*3)/3=21812
減1和減2與上例相同;
減4的原因是int類型的c占4個字節;
mysql> create table t4(c int, c2 char(30), c3 varchar(21812)) charset=utf8; Query OK, 0 rows affected (0.05 sec)mysql>
mysql> create table t5(c int, c2 char(30), c3 varchar(21813)) charset=utf8; 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 mysql>
?
最后讓我們來看一個例子
CREATE TABLE t6 ( id int, a VARCHAR(100) DEFAULT NULL, b VARCHAR(100) DEFAULT NULL, c VARCHAR(100) DEFAULT NULL, d VARCHAR(100) DEFAULT NULL, e VARCHAR(100) DEFAULT NULL, f VARCHAR(100) DEFAULT NULL, g VARCHAR(100) DEFAULT NULL, h VARCHAR(100) DEFAULT NULL, i VARCHAR(N) DEFAULT NULL ) CHARSET=utf8;
那么上面這條語句中的varchar(N)的最大值是多少呢?
讓我們來計算一下
每個NULL字段用1bit標識,10個字段都是default null,那么需要用(10+7)/8bit = 2 bytes存儲NULL標識位。int占用4個 byte。
(65535 - 1 - 2*8 ?-4 - 100*3*8 -2)/3=21037
mysql> CREATE TABLE t6 ( id int, a VARCHAR(100) DEFAULT NULL, b VARCHAR(100) DEFAULT NULL, c VARCHAR(100) DEFAULT NULL, d VARCHAR(100) DEFAULT NULL, e VARCHAR(100) DEFAULT NULL, f VARCHAR(100) DEFAULT NULL, g VARCHAR(100) DEFAULT NULL, h VARCHAR(100) DEFAULT NULL, i VARCHAR(21037) DEFAULT NULL ) CHARSET=utf8; Query OK, 0 rows affected (0.01 sec)mysql>
mysql> CREATE TABLE t7 ( id int, a VARCHAR(100) DEFAULT NULL, b VARCHAR(100) DEFAULT NULL, c VARCHAR(100) DEFAULT NULL, d VARCHAR(100) DEFAULT NULL, e VARCHAR(100) DEFAULT NULL, f VARCHAR(100) DEFAULT NULL, g VARCHAR(100) DEFAULT NULL, h VARCHAR(100) DEFAULT NULL, i VARCHAR(21038) DEFAULT NULL ) CHARSET=utf8; 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 mysql>
?可以看見多一個字符都報錯了。
varchar到底能存多少個字符?這與使用的字符集相關,latin1、gbk、utf8編碼存放一個字符分別需要占1、2、3個字節。
?
3、varchar物理存儲
在物理存儲上,varchar使用1到2個額外的字節表示實際存儲的字符串長度(bytes)。如果列的最大長度小于256個字節,用一個字節表示(標識)。如果最大長度大于等于256,使用兩個字節。
當選擇的字符集為latin1,一個字符占用一個byte
varchar(255)存儲一個字符,一共使用2個bytes物理空間存儲數據實際數據長度和數據值。
varchar(256)存儲一個字符,使用2 bytes表示實際數據長度,一共需要3 bytes物理存儲空間。
varchar對于不同的RDBMS引擎,有不通的物理存儲方式,雖然有統一的邏輯意義。對于mysql的不同存儲引擎,其實現方法與數據的物理存放方式也不同。
4、InnoDB中的varchar
InnoDB中varchar的物理存儲方式與InnoDB使用的innodb_file_format有關。早期的innodb_file_forma使用的Antelope文件格式,支持redundant和compact兩種row_format。從5.5開始或者InnoDB1.1,可以使用一種新的file format,Barracuda。Barracuda兼容Redundant,另外還支持dynamic和compressed兩種row_format.
當innodb_file_format=Antelope,ROW_FORMAT=REDUNDANT 或者COMPACT。
innodb的聚集索引(cluster index)僅僅存儲varchar、text、blob字段的前768個字節,多余的字節存儲在一個獨立的overflow page中,這個列也被稱作off-page。768個字節前綴后面緊跟著20字節指針,指向overflow pages的位置。
另外,在innodb_file_format=Antelope情況下,InnoDB中最多能存儲10個大字段(需要使用off-page存儲)。innodbd的默認page size為16KB,InnoDB單行的長度不能超過16k/2=8k個字節,(768+20)*10 < 8k。
當innodb_file_format=Barracuda, ROW_FORMAT=DYNAMIC 或者 COMPRESSED
innodb中所有的varchar、text、blob字段數據是否完全off-page存儲,根據該字段的長度和整行的總長度而定。對off-page存儲的列,cluster index中僅僅存儲20字節的指針,指向實際的overflow page存儲位置。如果單行的長度太大而不能完全適配cluster index page,innodb將會選擇最長的列作為off-page存儲,直到行的長度能夠適配cluster index page。
5、MyISAM中的varchar
對于MyISAM引擎,varchar字段所有數據存儲在數據行內(in-line)。myisam表的row_format也影響到varchar的物理存儲行為。
MyISAM的row_format可以通過create或者alter sql語句設為fixed和dynamic。另外可以通過myisampack生成row_format=compresse的存儲格式。
當myisam表中不存在text或者blob類型的字段,那么可以把row_format設置為fixed(也可以為dynamic),否則只能為dynamic。
當表中存在varchar字段的時候,row_format可以設定為fixed或者dynamic。使用row_format=fixed存儲varchar字段數據,浪費存儲空間,varchar此時會定長存儲。row_format為fixed和dynamic,varchar的物理實現方式也不同(可以查看源代碼文件field.h和field.cc),因而myisam的row_format在fixed和dynamic之間發生轉換的時候,varchar字段的物理存儲方式也將會發生變化。
?
參考資料:
http://dev.mysql.com/doc/refman/5.5/en/column-count-limit.html
<<MySQL技術內幕--InnoDB引擎第二版>>
作者:Atlas
出處:Atlas的博客?http://www.cnblogs.com/gomysql
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。本文版權歸作者所有,歡迎轉載,但請保留該聲明。如果您需要技術支持,本人亦提供有償服務。
