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,
  `col2` char(5) 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, 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(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字段類型的使用方法,並且給出了二者的對比以及適用場景。在實際生產情況,需要具體情況具體分析,合適的纔是最好的,希望這篇文章能給到大家參考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章