一. varchar存儲規則:html
5.0版本以上,varchar(20),指的是20字符,不管存放的是數字、字母仍是UTF8漢字(每一個漢字3字節),均可以存放20個,最大大小是65532字節
二. varchar和char 的區別:mysql
char是一種固定長度的類型,varchar則是一種可變長度的類型,它們的區別是: char(M)類型的數據列裏,每一個值都佔用M個字節,若是某個長度小於M,MySQL就會在它的右邊用空格字符補足.(在檢索操做中那些填補出來的空格字符將被去掉)在varchar(M)類型的數據列裏,每一個值只佔用恰好夠用的字節再加上一個用來記錄其長度的字節(即總長度爲L+1字節). sql
在MySQL中用來判斷是否須要進行對據列類型轉換的規則post
一、在一個數據表裏,若是每個數據列的長度都是固定的,那麼每個數據行的長度也將是固定的.編碼
二、只要數據表裏有一個數據列的長度的可變的,那麼各數據行的長度都是可變的.spa
一、限制規則指針
字段的限制在字段定義的時候有如下規則:code
a) 存儲限制orm
NULL標識位,若是varchar字段定義中帶有default null容許列空,則須要須要1bit來標識,每8個bits的標識組成一個字段。一張表中存在N個varchar字段,那麼須要(N+7)/8 (取整)bytes存儲全部的NULL標識位。htm
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。若定義的表長度超過這個值,則提示
二、計算例子
舉兩個例說明一下實際長度的計算。
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到底能存多少個字符?這與使用的字符集相關,latin一、gbk、utf8編碼存放一個字符分別須要佔一、二、3個字節。
三、varchar物理存儲
在物理存儲上,varchar使用1到2個額外的字節表示實際存儲的字符串長度(bytes)。若是列的最大長度小於256個字節,用一個字節表示(標識)。若是最大長度大於等於256,使用兩個字節。
當選擇的字符集爲latin1,一個字符佔用一個byte
varchar(255)存儲一個字符,一共使用2個bytes物理空間存儲數據實際數據長度和數據值。
varchar(256)存儲一個字符,使用2 bytes表示實際數據長度,一共須要3 bytes物理存儲空間。
varchar對於不一樣的RDBMS引擎,有不通的物理存儲方式,雖然有統一的邏輯意義。對於mysql的不一樣存儲引擎,其實現方法與數據的物理存放方式也不一樣。
四、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。
五、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引擎第二版>>