1. 程式人生 > 程式設計 >CHAR與VARCHAR詳解

CHAR與VARCHAR詳解

前言: 

前面寫過一篇介紹int型別的文章,一直想寫一篇介紹字串欄位型別的文章,一直拖著也沒思路要怎麼下手。最近多關注了下這方面的文章,決定還是把拖了好久的文章了結了吧。本篇文章主要會介紹字串型別char及varchar的用法及區別。

本文實驗環境為MySQL 5.7.23版本,儲存引擎為Innodb,sql_mode採用嚴格模式,字符集是utf8。

▍1.CHAR型別介紹

我們平時使用char型別定義欄位時,往往會指定其長度M,即char(M)。其實M指的是字元數,即這個欄位最多儲存多少個字元,M可不指定,預設為1,範圍是[0,255],單個字母、數字、中文等都是佔用一個字元。utf8字符集下一個中文字元佔用3個位元組。下面我們簡單測試下:

# 假設以如下建表語句建立測試表
CREATE TABLE `char_tb1` (
  `col1` char DEFAULT NULL,`col2` char(5) DEFAULT NULL,`col3` char(10) DEFAULT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

# 進入資料庫查詢建表語句如下 發現char(M) M可不指定,預設為1
mysql> show create table char_tb1\G
*************************** 1. row ***************************
       Table: char_tb1
Create Table: CREATE TABLE `char_tb1` (
  `col1` char(1) DEFAULT NULL,`col3` char(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

# 插入資料 可以看出M表示儲存的最大字元數,字母、數字、中文等都是佔用一個字元
mysql> insert into char_tb1 (col1) values ('a'),('1'),('王'),(']');
Query OK,4 rows affected (0.01 sec)
mysql> insert into char_tb1 (col1) values ('aa'),('12');
ERROR 1406 (22001): Data too long for column 'col1' at row 1
mysql> select * from char_tb1;
+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
| a    | NULL | NULL |
| 1    | NULL | NULL |
| 王   | NULL | NULL |
| ]    | NULL | NULL |
+------+------+------+
4 rows in set (0.00 sec)
mysql> insert into char_tb1 (col2) values ('abcd'),('王-123'),('^*123'),('12'),('一二三四五');
Query OK,5 rows affected (0.01 sec)
mysql> insert into char_tb1 (col2) values ('abcdef');
ERROR 1406 (22001): Data too long for column 'col2' at row 1
mysql> select * from char_tb1;
+------+-----------------+------+
| col1 | col2            | col3 |
+------+-----------------+------+
| a    | NULL            | NULL |
| 1    | NULL            | NULL |
| 王   | NULL            | NULL |
| ]    | NULL            | NULL |
| NULL | abcd            | NULL |
| NULL | 王-123          | NULL |
| NULL | ^*123           | NULL |
| NULL | 12              | NULL |
| NULL | 一二三四五      | NULL |
+------+-----------------+------+
9 rows in set (0.00 sec)

# 下面測試發現M的範圍是[0,255] 
mysql> alter table char_tb1 add column col4 char(0);
Query OK,0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table char_tb1 add column col5 char(255);
Query OK,0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table char_tb1 add column col5 char(256);
ERROR 1074 (42000): Column length too big for column 'col5' (max = 255); use BLOB or TEXT instead複製程式碼

▍2.VARCHAR型別介紹

同樣的,varchar(M)中的的M表示儲存的最大字元數,單個字母、數字、中文等都是佔用一個字元。varchar可儲存的長度範圍為0-65535位元組,此外,varchar需要使用1或者2個額外位元組記錄字串的長度:如果列的最大長度小於或等於255位元組,則只使用1個位元組表示,否則使用2個位元組。對於Innodb引擎,utf8字符集來說,單箇中文字元佔用3個位元組,所以varchar(M)中的M最大不會超過21845,即M的範圍是[0,21845),並且M必須指定。另外MySQL規定:單個欄位長度不大於65535位元組;單行最大限制為65535,這裡不包括TEXT、BLOB欄位。即單張表中的所有varchar欄位定義的長度之和不能大於65535,所以並不是所有varchar(M)欄位中的M都可以取到21844,下面我們來驗證下:

# 假設以如下建表語句建立測試表
CREATE TABLE `varchar_tb1` (
  `col1` varchar(0) DEFAULT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

# 檢視建表語句 增加欄位 發現M必須指定
mysql> show create table varchar_tb1\G
*************************** 1. row ***************************
       Table: varchar_tb1
Create Table: CREATE TABLE `varchar_tb1` (
  `col1` varchar(0) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> alter table varchar_tb1 add column col2 varchar;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1

# 下面測試證明M最大可取到21844
mysql> CREATE TABLE `varchar_tb2` (col1 varchar(21844));
Query OK,0 rows affected (0.04 sec)

mysql> CREATE TABLE `varchar_tb3` (col1 varchar(218445));
ERROR 1074 (42000): Column length too big for column 'col1' (max = 21845); use BLOB or TEXT instead

# 下面測試證明單行最大限制為65535位元組
mysql> CREATE TABLE `varchar_tb3` (col1 varchar(10));
Query OK,0 rows affected (0.04 sec)

mysql> alter table varchar_tb3 add column col2 varchar(21844);
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
mysql> alter table varchar_tb3 add column col2 varchar(21834);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,check the manual. You have to change some columns to TEXT or BLOBs
mysql> alter table varchar_tb3 add column col2 varchar(21833);
Query OK,0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table varchar_tb3\G
*************************** 1. row ***************************
       Table: varchar_tb3
Create Table: CREATE TABLE `varchar_tb3` (
  `col1` varchar(10) DEFAULT NULL,`col2` varchar(21833) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)複製程式碼

▍3.CHAR與VARCHAR比較

CHAR型別是定長的,MySQL總是根據定義的字串長度分配足夠的空間。當儲存CHAR值時,在它們的右邊填充空格以達到指定的長度,當檢索到CHAR值時,尾部的空格被刪除掉。

VARCHAR型別用於儲存可變長字串,儲存時,如果字元沒有達到定義的位數,也不會在後面補空格。但是,由於行是變長的,在UPDATE時可能使行變得比原來更長,這就導致需要做額外的工作。如果一個行佔用的空間增長,並且在頁內沒有更多的空間可以儲存,在這種情況下InnoDB需要分裂頁來使行可以放進頁內,這樣會增加碎片。

下面簡單總結下CHAR與VARCHAR欄位型別的適用場景:

CHAR適合儲存很短的字串,或者所有值都接近同一個長度。例如,CHAR非常適合儲存密碼的MD5值,因為這是一個定長的值。對於經常變更的資料,CHAR也比VARCHAR更好,因為定長的CHAR型別不容易產生碎片。對於非常短的列,CHAR比VARCHAR在儲存空間上也更有效率。例如用CHAR(1)來儲存只有Y和N的值,如果採用單位元組字符集只需要一個位元組,但是VARCHAR(1)卻需要兩個位元組,因為還有一個記錄長度的額外位元組。

下面這些情況下使用VARCHAR是合適的:字串很長或者所要儲存的字串長短不一,差別很大;字串列的最大長度比平均長度大得多;列的更新很少,所以碎片不是問題。

額外說明下,我們在定義欄位最大長度時應該按需分配,提前做好預估。特別是對於VARCHAR欄位,有人認為反正VARCHAR資料型別是根據實際的需要來分配長度的,還不如給大一點呢。但事實不是這樣的,比如現在需要儲存一個地址資訊,根據評估,只要使用100個字元就可以了,我們可以使用VARCHAR(100)或VARCHAR(200)來儲存,雖然它們用來儲存90個字元的資料,其儲存空間相同,但是對於記憶體的消耗是不同的。更長的列會消耗更多的記憶體,因為MySQL通常會分配固定大小的記憶體塊來儲存內部值,尤其是使用記憶體臨時表進行排列或者操作時會特別糟糕。所以我們在分配VARCHAR資料型別時仍然不能夠太過於慷慨。還是要評估實際需要的長度,然後選擇一個最長的欄位來設定字元長度。如果為了考慮冗餘,可以留10%左右的字元長度。千萬不能認為VARCHAR是根據實際長度來分配儲存空間,而隨意的分配長度,或者說乾脆使用最大的字元長度。

總結: 

本文分別介紹了CHAR與VARCHAR欄位型別的使用方法,並且給出了二者的對比以及適用場景。在實際生產情況,需要具體情況具體分析,合適的才是最好的,希望這篇文章能給到大家參考。

gongzhonghao