关于MySQL的类型

一般情况下,应该尽量使用可以正确存储数据的最小数据类型。简单数据类型的操作通常需要更少的CPU周期。例如,整型比字符操作代价更低。这里有两个例子:一个是应该使用MySQL内建的类型而不是字符串来存储日期和时间,另外一个是 应该用整型存储IP地址。

通常情况下最好指定列为 NOT NULL,除非真的需要存储 NULL值。


整数类型

整数类型有可选的 UNSIGNED 属性,表示不允许负值,这大致可以使正数的上限提高一倍。例如 TINYINT UNSIGNED 可以存储的范围是0~255,而TINYINT的存储范围是−128~127。

实数类型

有多种方法可以指定浮点列所需要的精度,这会使得MySQL悄悄选择不同的数据类型,或者在存储时对值进行取舍。这些精度定义是非标准的,建议只指定数据类型,不指定精度。

因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用 DECIMAL——例如 存储财务
数据。假设要存储财务数据精确到万分之一分,则可以把所有金额乘以一百万,然后将结果存储在 BIGINT 里,这样可以同时避免浮点存储计算不精确和 DECIMAL 精确计算代价高的问题。

字符串类型

VARCHAR和CHAR类型

VARCHAR:

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

VARCHAR 需要使用1或2个额外字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只 使用1个字节表示,否则使用2个
字节。

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

CHAR:

CHAR 类型是定长的:MySQL总是根据定义的字符串长度分配足够的空间。当存储 CHAR 值时, MySQL会删除所有的末尾空格。

CHAR 适合存储很短的字符串,或者所有值都接近同一个长度。例如, CHAR 非常适合存储密码的 MD5 值,因为这是一个定长的值。对于经常变更的数据, CHAR 也比 VARCHAR 更好,因为定长的 CHAR 类型不容易产生碎片。

当需要存储二进制数据,并且希望MySQL使用字节码而不是字符进行比较时,这些类型是非常有用的。 二进制比较的优势并不仅仅体现在大小写敏感上。MySQL比较 BINARY 字符串时,每次按一个字节, 并且根据该字节的数值进行比较。因此,二进制比较比字符比较简单很多,所以也就更快。


使用 VARCHAR(5) 和 VARCHAR(200) 存储’ hello’ 的空间开销是一样的。那么使用更短的列有 什么优势吗? 事实证明有很大的优势。更长的列会消耗更多的内存,因为MySQL通常会分配固定大小的 内存块来保存内部值。尤其是使用内存临时表进行排序或操作时会特别糟糕。在利用磁盘临时表进行排 序时也同样糟糕。 所以最好的策略是只分配真正需要的空间。

BLOB和TEXT类型

BLOB 和 TEXT 都是为存储很大的数据而设计的字符串数据类型,分别采用二进制和字符方式存储。

当 BLOB 和 TEXT 值太大时,InnoDB会使用专门的“外部”存储区域来进行存储,此时每个值在行内 需要1~4个字节存储一个指针,然后在外部存储区域存储实际的实际值。

BLOB 和 TEXT 家族之间仅有的不同是 BLOB 类型存储的是二进制数据,没有排序规则或字符集,而 TEXT 类型有字符集和排序规则。

MySQL对 BLOB 和 TEXT 列进行排序与其他类型是不同的:它只对每个列的最前 max_sort_length 字节 而不是整个字符串做排序。如果只需要排序前面一小部分字符,则可以减小 max_sort_length 的配置, 或者使用 ORDER BY SUSTRING(column,length)。

使用枚举(ENUM)代替字符串类型

有时候可以使用枚举列代替常用的字符串类型。枚举列可以把一些不重复的字符串存储成一个预定义的集合。

如果使用数字作为 ENUM 枚举常量,双重性很容易导致混乱,例如 ENUM(‘1’,‘2’,‘3’)。建议尽量避免这么做。

枚举字段是按照内部存储的整数而不是定义的字符串进行排序的。

枚举最不好的地方是,字符串列表是固定的,添加或删除字符串必须使用 ALTER TABLE。因此,对于 一系列未来可能会改变的字符串,使用枚举不是一个好主意,除非能接受只在列表末尾添加元素。

日期和时间类型

MySQL能存储的最小时间粒度为秒。

DATETIME:
这个类型能保存大范围的值,从1001年到9999年,精度为秒。它把日期和时间封装到格式为 YYYYMMDDHHMMSS的整数中,与时区无关。使用8个字节的存储空间。

TIMESTAMP:
TIMETAMP 类型保存了从1970年1月1日午夜(格林尼治标准时间)以来的秒数。TIMESTAMP 只使用4个字节的存储空间,因此它的范围比 DATETIME 小得多:只能表示从1970年到 2038年。

TIMESTAMP 显示的值也依赖于时区。MySQL服务器、操作系统,以及客户端连接都有时区。

默认情况下,如果插入时没有指定第一个 TIMESTAMP 列的值,MySQL则设置这个列的值为当前时间。插入一行记录时,MySQL默认也会更新第一个 TIMESTAMP 列的值(除非在 UPDATE 语句中明确指定了值)。TIMESTAMP 列默认为 NOT NULL,这也和其他的数据类型不一样。

除了特殊行为之外,通常也应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。


如果需要存储比秒更小粒度的日期和时间值怎么办?MySQL目前没有提供合适的数据类型,但是可以使 用自己的存储格式:可以使用 BIGINT 类型存储微秒级别的时间截,或者使用 DOUBLE 存储秒之后的小数部分。

位数据类型

BIT:
可以使用 BIT 列在一列中存储一个或多个true/false值。BIT 列的最大长度是64个位。

MyISAM会打包存储所有的 BIT 列,所以17个单独的BIT列只需要17个位存储(假设没有可为 NULL 的列),这样MyISAM只使用3个字节就能存储这17个BIT列。Memory和InnoDB,为每个 BIT 列使用一个足够存储的最小整数类型来存放,所以不能节省存储空间。

MySQL把 BIT 当作字符串类型,而不是数字类型。在数字上下文的场景中检索时,结果将是位字符串转换成的数字。例如,如果存储一个值 b’00111001’(二进制值等于57)到 BIT(8) 的列并且检索它,得到的内容是字 符码为57的字符串。也就是说得到ASCII码为57的字符“9”。但是在数字上下文场景中,得到的是数字57。

应该谨慎使用 BIT 类型。对于大部分应用,最好避免使用这种类型。

如果想在一个bit的存储空间中存储一个true/false值,另一个方法是创建一个可以为空的 CHAR(0) 列。该列可以保存空值( NULL) 或者长度为零的字符串(空字符串)。

SET:
如果需要保存很多true/false值,可以考虑合并这些列到一个 SET 数据类型,它在MySQL内部是以一系 列打包的位的集合来表示的。有FIND_IN_SET() 和 FIELD() 这样的函数,方便地在查询中使用。它的主要缺点是改变列的定义的代价较高:需要 ALTER TABLE,这对大表来说是非常昂贵的操作。一般来说,也无法在SET列上通过索引查找。


选择标识符

整数类型

整数通常是标识列最好的选择,因为它们很快并且可以使用 AUTO_INCREMENT。

ENUM和SET类型

对于标识列来说, EMUM 和 SET 类型通常是一个糟糕的选择。

字符串类型

如果可能,应该避免使用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。

MD5()、SHA1() 或者 UUID() 产生的字符串。这些函数生成的新值会任意分布在很大的空间内,这会导致 INSERT 以及一些 SELECT 语句变得很慢:

  • 因为插入值会随机地写到索引的不同位置,所以使 得 INSERT 语句更慢。这会导致页分裂、磁盘随机访问,以及对于聚簇存储引擎产生聚簇索引碎片。
  • SELECT 语句会变得更慢,因为逻辑上相邻的行会分布在磁盘和内存的 不同地方。
  • 随机值导致缓存对所有类型的查询语句效果都很差,因为会使得缓存赖以工作的访问局部性原理失效。如果整个数据集都一样的“热”,那么缓存任何一部分特定数据到内存都没有好处;如果工 作集比内存大,缓存将会有很多刷新和不命中。

你可能感兴趣的:(关于MySQL的类型)