《高性能MySQL》笔记-枚举(ENUM)类型

有时候可以使用枚举列代替常用的字符串类型。枚举列可以把一些不重复的字符串存储成一个预定义的集合。MySQL在存储枚举时非常紧凑,会根据列表值的数量压缩到一个或者两个字节中。MySQL在内部会将每个值在列表中的位置保存为整数,并且在表的.frm文件中保存“数字-字符串”映射关系的“查找表”。下面有一个例子:
《高性能MySQL》笔记-枚举(ENUM)类型
这三行数据实际存储为整数,而不是字符串。可以通过在数字上下文环境检索看到这个双重属性:
《高性能MySQL》笔记-枚举(ENUM)类型
如果使用数字作为ENUM枚举常量,这种双重性很容易导致混乱,例如ENUM(‘1’,’2’,’3’)。建议尽量避免这么做。
另外一个让人吃惊的地方是,枚举字段是按照内部存储的整数而不是定义的字符串进行排序的:
《高性能MySQL》笔记-枚举(ENUM)类型
一种绕过这种限制的方式是按照需要的顺序来定义枚举列。另外也可以在查询中使用FIELD()函数显示地指定排序顺序,但这会导致MySQL无法利用索引消除排序。
《高性能MySQL》笔记-枚举(ENUM)类型
如果在定义时就是按照字母的顺序,就没有必要这么做了。
枚举最不好的地方是,字符串列表是固定的,添加或删除字符串必须使用ALTER TABLE。因此,对于一些列未来可能会改变的字符串,使用枚举不是一个好主意,除非能接受只在列表末尾添加元素,这样在MySQL5.1中就可以不用重建整个表来完成修改。
由于MySQL把每个枚举值保存为整数,并且必须进行查找才能转换为字符串,所以枚举列有一些开销。通常枚举的列表都比较小,所以开销还可以控制,但也不能保证一直如此。在特定情况下,把CHAR/VARCHAR列与枚举列进行关联可能会比直接关联CHAR/VARCHAR列更慢。
为了说明这个情况,我们对一个应用中的一张表进行基准测试,看看在MySQL中执行上面说的关联的速度如何。该表有一个很大的主键:
《高性能MySQL》笔记-枚举(ENUM)类型
这个表有11万行数据,只有10MB大小,所以可以完全载入内存。service列包含了5个不同的值,平均长度为4个字符,method列包含了71个值,平均长度为20个字符。我们复制一下这个表,但是把service和method字段换成枚举类型,表结构如下:
《高性能MySQL》笔记-枚举(ENUM)类型
然后我们用主键列关联这两个表,下面是所使用的查询语句:
《高性能MySQL》笔记-枚举(ENUM)类型
我们用VARCHAR和ENUM分别测试了这个语句,结果入标4-1所示。
《高性能MySQL》笔记-枚举(ENUM)类型
从上年的结果可以看到,当把列都转换成ENUM以后,关联变得很快。但是当VARCHAR列和ENUM列进行关联时则慢很多。在本例中,如果不是必须和VARCHAR列进行关联,那么转换这些列为ENUM就是个好主意。这是一个通用的设计实践,在“查找表”时采用整数主键而避免采用基于字符串的值进行关联。
然而,转换列为枚举型还有另一个好处。根据SHOW TABLE STATUS命令输出结果中Data_length列的值,把这两列转换为ENUM可以让表的大小缩小1/3。在某些情况下,即使可能出现ENUM和VARCHAR进行关联的情况,这也是值得的。同样,转换后主键也只有原来的一半大小了。因为这是InnoDB表,如果表上有其他索引,减小主键大小会使非主键索引也变得更小。