MySQL 数据类型选择和优化

本贴最后更新于 1140 天前,其中的信息可能已经时移俗易

MySQL 支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。

一、选择数据类型的原则

  • 尽量小的存储空间

    一般情况下,尽量应该选择可以正确存储数据的最小数据类型,因为更小的数据类型可以占用更小的磁盘、内存和 CPU 缓存;处理数据时需要的 CUP 周期更少;但如果低估了数据占用的空间,后期需要更多空间时,对列的数据类型进行修改,将会是非常耗时的过程(假如表中数据量非常多);

  • 尽量选择简单类型

    CUP 通常在处理简单数据类型的时候执行计算需要的时间会更短,比如,整数比字符操作的代价更低(因为字符集处理和校对规则如排序比整数要复杂);举例:应用应该使用 MySQL 的内建日期类型来存储日期含义的数据,而非使用字符串来存储;另一个例子是 IP 地址也可以使用整数来存储,比字符串存储效率高很多;

  • 尽量避免 Null

    在应用程序不需要保存 Null 值时,尽量不要将列设置成可为空;因为 Null 值在 MySQL 中很难优化,这样的列使得索引、索引统计和值比较都更复杂;当可为空的列,被设置成索引时,每个索引记录都会需要一个额外的字节;

二、数值类型

类型 大小 范围(有符号) 范围(无符号) 用途
TINYINT 1 byte (-128,127) (0,255) 小整数值
SMALLINT 2 bytes (-32 768,32 767) (0,65 535) 大整数值
MEDIUMINT 3 bytes (-8 388 608,8 388 607) (0,16 777 215) 大整数值
INT 或 INTEGER 4 bytes (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值
BIGINT 8 bytes (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) (0,18 446 744 073 709 551 615) 极大整数值
FLOAT 4 bytes (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) 0,(1.175 494 351 E-38,3.402 823 466 E+38) 单精度浮点数值
DOUBLE 8 bytes (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 双精度浮点数值
DECIMAL 对 DECIMAL(M,D) ,如果 M>D,为 M+2 否则为 D+2 依赖于 M 和 D 的值 依赖于 M 和 D 的值 可精确计算的小数值

数值类型包括整数类型和实数类型。

整数类型有可选的 UNSIGNED 属性,表述不允许负值,每种类型的表示数值个数一样,但是正数范围扩大了一倍;有符号数和无符号数的存储空间相同;

需要注意:MySQL 可以为正数设置指定宽度,如 INT(11),对大多数应用是没有实际意义的,因为这样设置并不会影响该类型实际在磁盘上存储的空间,也不会影响类型可以表示的整数范围,而是只限制了连接 MySQL 服务的客户端对于该类型显示的字符个数;所以 INT(1)和 INT(20)在存储和计算处理上,是等价的;

对于 DECIMAL 而言,MySQL5.0 及以后,将数字打包成一个二进制字符串(每 4 个字节存 9 位数字),比如:DECIMAL(18,9),小数点两边各存 9 个数字,小数点前占用 4 个字节,后占用 4 个字节,小数点本身占用一个字节,共 9 个字节;允许最多 65 个数字(低版本是 254 个数字限制);
因为需要额外的空间和计算开销,应该尽量在需要对小数进行精确计算时,才选用这种类型,比如财务数据;但是对于这种情况,可以使用 BIGINT 来代替 DECIMAL,只需要根据货币单位和需要精确的小数位,将小数乘以相应的倍数,就可以变成整数来计算和存储;

字符串类型

类型 大小 用途
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYBLOB 0-255 bytes 不超过 255 个字符的二进制字符串
TINYTEXT 0-255 bytes 短文本字符串
BLOB 0-65 535 bytes 二进制形式的长文本数据
TEXT 0-65 535 bytes 长文本数据
MEDIUMBLOB 0-16 777 215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215 bytes 中等长度文本数据
LONGBLOB 0-4 294 967 295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295 bytes 极大文本数据

VARCHAR 需要额外的 1 个或者 2 个字节来保存字符串的长度信息,如果字符串长度小于等于 255,则使用 1 个额外字节,大于 255 使用 2 个,因此该类型最长是 65535;
使用场景:字符串的最大长度,可能比平均长度长很多的列;列很少更新;使用 UTF-8 这样较复杂的字符集每个字符原本使用的字节数就可能不同;
CHAR 使用与存储较短的字符串,且长度想当;
对于单值字符或者极短的字符串,CHAR 比 VARCHAR 有效,例如使用 CHAR(1)存储'Y'或'N',只需要一个字符,如果使用单字节字符集,只占用一个字节,而 VARCHAR(1)却需要额外的一个表示长度的字节,共两个字节才能表示;
另外,CHAR 在遇到字符串尾部有空格时,会忽略掉(不保留),而 VARCHAR 则会保留尾部空格;

日期类型

类型 大小( bytes) 范围 格式 用途
DATE 3 1000-01-01/9999-12-31 YYYY-MM-DD 日期值
TIME 3 '-838:59:59'/'838:59:59' HH:MM:SS 时间值或持续时间
YEAR 1 1901/2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00/9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:00/2038 结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038 年 1 月 19 日 凌晨 03:14:07 YYYYMMDD HHMMSS 混合日期和时间值,时间戳

在足够表示的情况下,推荐使用 TIMESTAMP 表示时间,因为它为 DATETIME 空间效率更高;
另外,很多人会将时间戳存储为整数值存储,这样不方便处理,所以不推荐;
对于想要保存比秒更小的时间精度时,可以使用 BIGINT 在存储微妙级别的时间戳;

数据类型属性

上面总结了 MySQL 中的数据类型,下面再来总结一些常用的属性。

  • 1.auto_increment

auto_increment 能为新插入的行赋一个唯一的整数标识符。为列赋此属性将为每个新插入的行赋值为上一次插入的 ID+1。

MySQL 要求将 auto_increment 属性用于作为主键的列。此外,每个表只允许有一个 auto_increment 列。例如:

代码如下:

id smallint not null auto_increment primary key

  • 2.binary

binary 属性只用于 char 和 varchar 值。当为列指定了该属性时,将以区分大小写的方式排序。与之相反,忽略 binary 属性时,将使用不区分大小写的方式排序。例如:

代码如下:

hostname char(25) binary not null

  • 3.default

default 属性确保在没有任何值可用的情况下,赋予某个常量值,这个值必须是常量,因为 MySQL 不允许插入函数或表达式值。此外,此属性无法用于 BLOB 或 TEXT 列。如果已经为此列指定了 NULL 属性,没有指定默认值时默认值将为 NULL,否则默认值将依赖于字段的数据类型。例如:

代码如下:

subscribed enum('0', '1') not null default '0'

  • 4.index

如果所有其他因素都相同,要加速数据库查询,使用索引通常是最重要的一个步骤。索引一个列会为该列创建一个有序的键数组,每个键指向其相应的表行。以后针对输入条件可以搜索这个有序的键数组,与搜索整个未索引的表相比,这将在性能方面得到极大的提升。

代码如下:

create table employees
(
id varchar(9) not null,
firstname varchar(15) not null,
lastname varchar(25) not null,
email varchar(45) not null,
phone varchar(10) not null,
index lastname(lastname),
primary key(id)
);

我们也可以利用 MySQL 的 create index 命令在创建表之后增加索引:

代码如下:

create index lastname on employees (lastname(7));

这一次只索引了名字的前 7 个字符,因为可能不需要其它字母来区分不同的名字。因为使用较小的索引时性能更好,所以应当在实践中尽量使用小的索引。

  • 5.not null

如果将一个列定义为 not null,将不允许向该列插入 null 值。建议在重要情况下始终使用 not null 属性,因为它提供了一个基本验证,确保已经向查询传递了所有必要的值。

  • 6.null

为列指定 null 属性时,该列可以保持为空,而不论行中其它列是否已经被填充。记住,null 精确的说法是“无”,而不是空字符串或 0。

  • 7.primary key

primary key 属性用于确保指定行的唯一性。指定为主键的列中,值不能重复,也不能为空。为指定为主键的列赋予 auto_increment 属性是很常见的,因为此列不必与行数据有任何关系,而只是作为一个唯一标识符。主键又分为以下两种:

(1)单字段主键

如果输入到数据库中的每行都已经有不可修改的唯一标识符,一般会使用单字段主键。注意,此主键一旦设置就不能再修改。

(2)多字段主键

如果记录中任何一个字段都不可能保证唯一性,就可以使用多字段主键。这时,多个字段联合起来确保唯一性。如果出现这种情况,指定一个 auto_increment 整数作为主键是更好的办法。

  • 8.unique

被赋予 unique 属性的列将确保所有值都有不同的值,只是 null 值可以重复。一般会指定一个列为 unique,以确保该列的所有值都不同。例如:

代码如下:

email varchar(45) unique
  • 9.zerofill

zerofill 属性可用于任何数值类型,用 0 填充所有剩余字段空间。例如,无符号 int 的默认宽度是 10;因此,当“零填充”的 int 值为 4 时,将表示它为 0000000004。例如:

代码如下:

orderid int unsigned zerofill not null
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    691 引用 • 535 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...