第4章 Schema与数据类型优化

4.1 选择优化的数据类型

4.1.3 字符串类型

VARCHAR和CHAR类型

VARCHAR和CHAR是两种最主要的字符串类型。不幸的是,很难精确地解释这些值是怎么存储在磁盘和内存中的,因为这跟存储引擎的具体实现有关。下面的描述假设使用的存储引擎是InnoDB和/或者MyISAM。如果使用的不是这两种存储引擎,请参考所使用的存储引擎的文档。

先看看VARCHAR和CHAR值通常在磁盘上怎么存储。请注意,存储引擎存储CHAR或者VARCHAR值的方式在内存中和在磁盘上可能不一样,所以MySQL服务器从存储引擎读出的值可能需要转换为另一种存储格式。下面是关于两种类型的一些比较。

VARCHAR

VARCHAR类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型更节省空间,因为它仅使用必要的空间(例如,越短的字符串使用越少的空间)。有一种情况例外,如果MySQL表使用ROW_FORMAT=FIXED创建的话,每一行都会使用定长存储,这会很浪费空间。

VARCHAR需要使用1或2个额外字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只使用1个字节表示,否则使用2个字节。假设采用latin1字符集,一个VARCHAR(10)的列需要11个字节的存储空间。

VARCHAR(1000)的列则需要1002个字节,因为需要2个字节存储长度信息。VARCHAR节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要做额外的工作。如果一个行占用的空间增长,并且在页内没有更多的空间可以存储,在这种情况下,不同的存储引擎的处理方式是不一样的。例如,MyISAM会将行拆成不同的片段存储,InnoDB则需要分裂页来使行可以放进页内。其他一些存储引擎也许从不在原数据位置更新数据。

下面这些情况下使用VARCHAR是合适的:字符串列的最大长度比平均长度大很多;列的更新很少,所以碎片不是问题;使用了像UTF-8这样复杂的字符集,每个字符都使用不同的字节数进行存储。

在5.0或者更高版本,MySQL在存储和检索时会保留末尾空格。但在4.1或更老的版本,MySQL会剔除末尾空格。

CHAR

CHAR类型是定长的:MySQL总是根据定义的字符串长度分配足够的空间。当存储CHAR值时,MySQL会删除所有的末尾空格(在MySQL 4.1和更老版本中VARCHAR也是这样实现的——也就是说这些版本中CHAR和VARCHAR在逻辑上是一样的,区别只是在存储格式上)。CHAR值会根据需要采用空格进行填充以方便比较。

CHAR适合存储很短的字符串,或者所有值都接近同一个长度。例如,CHAR非常适合存储密码的MD5值,因为这是一个定长的值。对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片。对于非常短的列,CHAR比VARCHAR在存储空间上也更有效率。例如用CHAR(1)来存储只有Y和N的值,如果采用单字节字符集(5)只需要一个字节,但是VARCHAR(1)却需要两个字节,因为还有一个记录长度的额外字节。

CHAR类型的这些行为可能有一点难以理解,下面通过一个具体的例子来说明。

首先,我们创建一张只有一个CHAR(10)字段的表并且往里面插入一些值:

mysql> CREATE TABLE char_test( char_col CHAR(10));

mysql> INSERT INTO char_test(char_col) VALUES-> ('string1'), (' string2'), ('string3 ')

当检索这些值的时候,会发现string3末尾的空格被截断了。

高性能mysql 豆瓣 高性能mysql第三版在线阅读_字符串

如果用VARCHAR(10)字段存储相同的值,可以得到如下结果(6):

高性能mysql 豆瓣 高性能mysql第三版在线阅读_MySQL_02

数据如何存储取决于存储引擎,并非所有的存储引擎都会按照相同的方式处理定长和变长的字符串。Memory引擎只支持定长的行,即使有变长字段也会根据最大长度分配最大空间(7)。不过,填充和截取空格的行为在不同存储引擎都是一样的,因为这是在MySQL服务器层进行处理的。

 

一般来说:用的比较多的是varchar:因为库中存储的数据大多都是不定长的,而varchar可以根据数据长度而使用不同的空间,只不过要多使用一个字节用来记录字符串长度;

char一般是用来保存字典的code值(男或女,地区代码之类)/表中的id值; 因为这些数据的长度是1位/32/或者固定的几位.

4.1.7 特殊类型数据

另一个例子是一个IPv4地址。人们经常使用VARCHAR(15)列来存储IP地址。然而,它们实际上是32位无符号整数,不是字符串。用小数点将地址分成四段的表示方法只是为了让人们阅读容易。所以应该用无符号整数存储IP地址。MySQL提供INET_ATON()和INET_NTOA()函数在这两种表示方法之间转换。

例如:

测试用例:
       select INET_ATON('172.25.43.55') from demo_test_del;
得到值:
      2887330615
      select  INET_NTOA(2887330615) from demo_test_del;
  得到值:
      172.25.43.55

附录:

(5) 记住字符串长度定义不是字节数,是字符数。多字节字符集会需要更多的空间存储单个字符。
(6) string3尾部的空格还在。——译者注
(7) Percona Server里的Memory引擎支持变长的行。

说明:当前内容为从书中摘抄部分文字,添加部分例子,已记录自己所得.如有侵权,请联系删除!