JavaWeb基礎知識
一. varchar儲存規則:
4.0版本以下,varchar(20),指的是20位元組,如果存放UTF8漢字時,只能存6個(每個漢字3位元組)
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、只要資料表裡有一個數據列的長度的可變的,那麼各資料行的長度都是可變的.
3、如果某個資料表裡的資料行的長度是可變的,那麼,為了節約儲存空間,MySQL會把這個資料表裡的固定長度型別的資料列轉換為相應的可變長度型別.例外:長度小於4個字元的char資料列不會被轉換為varchar型別
ps :被問到一個問題:MySQL中varchar最大長度是多少?這不是一個固定的數字。本文簡要說明一下限制規則。 |
1、限制規則
欄位的限制在欄位定義的時候有以下規則:
a) 儲存限制
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 byte。
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位元組的空間。
如果資料表只有一個varchar欄位且該欄位NOT NULL,那麼該varchar欄位的最大長度為65533個位元組,即65535-2=65533byte
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>
b) 編碼長度限制
字元型別若為gbk,每個字元最多佔2個位元組,最大長度不能超過32766;
字元型別若為utf8,每個字元最多佔3個位元組,最大長度不能超過21845。
若定義的時候超過上述限制,則varchar欄位會被強行轉為text型別,併產生warning。
c) 行長度限制
導致實際應用中varchar長度限制的是一個行定義的長度。 MySQL要求一個行的定義長度不能超過65535。若定義的表長度超過這個值,則提示
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。
2、計算例子
舉兩個例說明一下實際長度的計算。
a) 若一個表只有一個varchar型別,如定義為
create table t4(c varchar(N)) charset=gbk;
則此處N的最大值為(65535-1-2)/2= 32766。
減1的原因是實際行儲存從第二個位元組開始;
減2的原因是varchar頭部的2個位元組表示長度;
除2的原因是字元編碼是gbk。
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個位元組;
減30*3的原因是char(30)佔用90個位元組,編碼是utf8。
如果被varchar超過上述的b規則,被強轉成text型別,則每個欄位佔用定義長度為11位元組,當然這已經不是varchar了。
則此處N的最大值為 (65535-1-2-4-30*3)/3=21812,例子如下:
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 - 28 -4 - 1003*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欄位的物理儲存方式也將會發生變化。