1、varchar(N)的邏輯意義
從MySQL4.1開始,varchar (N)中的N指的是該字段最多能存儲多少個字符(characters),不是字節數。
不管是一個中英文字符或者數字、或者一個漢字,都當做一個字符。在4.1之前,N表示的是最大存儲的字節數(bytes)。
2、varchar(N)到底能存多長的數據
在mysql reference manual上,varchar最多能存儲65535個字節的數據。varchar 的最大長度受限於最大行長度(max row size,65535bytes)。65535並不是一個很精確的上限,可以繼續縮小這個上限。65535個字節包括所有字段的長度,變長字段的長度標識(每個變長字段額外使用1或者2個字節記錄實際數據長度)、NULL標識位的累計。
NULL標識位,如果varchar字段定義中帶有default null允許列空,則需要需要1bit來標識,每8個bits的標識組成一個字段。一張表中存在N個varchar字段,那麼需要(N+7)/8 (取整)bytes存儲所有的NULL標識位。
如果數據表只有一個varchar字段且該字段DEFAULT NULL,那麼該varchar字段的最大長度為65532個字節,即65535-2-1=65532 bytes。
復制代碼 代碼如下:CREATE TABLE `vchar1` ( `name` VARCHAR(65533) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `vchar2` ( `name` VARCHAR(65533) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
如果數據表只有一個varchar字段且該字段NOT NULL,那麼該varchar字段的最大長度為65533個字節,即65535-2=65533bytes。
復制代碼 代碼如下:CREATE TABLE `vchar3` ( `name` VARCHAR(65532) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `vchar4` ( `name` VARCHAR(65532) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
來個略微復雜點的表結構,->
復制代碼 代碼如下:CREATE TABLE `tv` (
`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
) ENGINE=InnoDB DEFAULT CHARSET=latin1
`i` varchar(N) DEFAULT NULL中N最大值可以為多少?
這樣計算:已知確定的字段長度為100*8 bytes,8個varchar(100)字段總共需要變長字段表示字節為1*8=8 bytes。每個NULL字段用1bit標識,9個字段都是default null,那麼需要用(9+7)/8bit = 2 bytes存儲NULL標識位。65535-100*8-1*8-2 = 64725 > 256, 那麼字段i的最大長度為64725 - 2 =64723 bytes,即N=64723 。
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字段的物理存儲方式也將會發生變化。