CHAR 和 VARCHAR 类型相似,但在存储的检索时有区别,同时在最大长度定义与尾部空格上是否保留也有区别。
CHAR 和 VARCHAR 声明时格式为 CHAR(num) 和 VARCHAR(num),这里的 num 表示的是你想存储的最大字符数。注意是字符数,而不是字节数。
CHAR(num),存储时长度是固定的,num值的范围在[0, 255](0和255均可取值)。当存储的字符不足时,会右边补足空格。
VARCHAR(num),长度定义为变长,num值的范围是[0, 65535]。需要注意的是这里的 65535 是所有列的总和不能超过的值,也就是说当只有一个 VARCHAR 列时可选的最大值。
即 VARCHAR 仅使用必要空间,一般情况下,它比 CHAR 要更节省空间。
VARCHAR 会额外使用1到2个字节来记录字符串的长度。当长度小于等于255字节时,使用1个字节,大于255字节时使用2个字节。
此外,VARCHAR 在更新时需要做额外的操作,因为如果更新的操作是将行变得更长,并且在页内没有更多的空间可以存储,在这种情况下,不同的存储引擎的处理方式不一样。例如 MyISAM 会将行拆成不同的片段存储,InnoDB则需要分裂页来使行可以放进页内。总而言之都会带开额外的操作。而 CHAR 因为每次分配的空间都是固定的,因为不会有这个问题存在。
所以,当长度比较小或者长度固定时,更适合用 CHAR 类型来存储,因为不会经常变动,且节省了空间。
CHAR 类型不足长度时会在右侧补足空格,但在检索时会自动移除掉右边的空格(这里的移除不仅是自动补足的,实际插入的也会移除)。但当 sql_mode 设置为 PAD_CHAR_TO_FULL_LENGTH 时,则不会被移除。示例如下(mysql版本5.7,后同):
CREATE TABLE t1 (c1 CHAR(10));
INSERT INTO t1 (c1) VALUES('xy');
set sql_mode = '';
show variables like 'sql_mode';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sql_mode | |
+---------------+-------+
SELECT concat('(', c1, ')'), CHAR_LENGTH(c1) FROM t1;
+----------------------+-----------------+
| concat('(', c1, ')') | CHAR_LENGTH(c1) |
+----------------------+-----------------+
| (xy) | 2 |
+----------------------+-----------------+
SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
mysql> SELECT concat('(', c1, ')'), CHAR_LENGTH(c1) FROM t1;
+----------------------+-----------------+
| concat('(', c1, ')') | CHAR_LENGTH(c1) |
+----------------------+-----------------+
| (xy ) | 10 |
+----------------------+-----------------+
VARCHAR 不会填充空格,而对于实际插入的尾部空格,在4.1版本之前,VHARCHAR的处理方式和 CHAR 是一致的,但是之后的版本中,VARCHAR则会保留空格。示例如下:
CREATE TABLE vc (v VARCHAR(4), c CHAR(4));
INSERT INTO vc VALUES ('ab ', 'ab ');
SELECT CONCAT('(', v, ')'), CONCAT('(', c, ')') FROM vc;
+---------------------+---------------------+
| CONCAT('(', v, ')') | CONCAT('(', c, ')') |
+---------------------+---------------------+
| (ab ) | (ab) |
+---------------------+---------------------+
使用 VARCHAR(5) 和 VARCHAR(10) 存储 hello 的空间开销是一样的,那实际使用更短的列有什么优势吗?
更长的列会消耗更多的内存。因为 MySQL 通常会分配固定大小的内存块来保存内部值,尤其是使用内存临时表进行排序或操作时。在利用磁盘临时表进行排序也同样糟糕。所以,最好的策略是只分配真正需要的空间。from 《高性能MySQL》