跳至主要內容

MySQL 数据类型

钝悟...大约 9 分钟数据库关系型数据库mysql数据库关系型数据库mysql数据类型

MySQL 数据类型

概述

数据类型在 MySQL 中扮演着至关重要的角色,它定义了表中每个字段可以存储的数据种类和格式。

MySQL 支持多种类型,大致可以分为三类:数值、时间和字符串类型。

数值类型

MySQL 支持所有标准 SQL 数值数据类型。这些类型包括严格数值数据类型 (INTEGERSMALLINTDECIMALNUMERIC),以及近似数值数据类型 (FLOATREALDOUBLE PRECISION)。

作为 SQL 标准的扩展,MySQL 也支持整数类型 TINYINTMEDIUMINTBIGINT。下面的表显示了需要的每个整数类型的存储和范围。

类型大小 (字节)范围(有符号)范围(无符号)用途
TINYINT1(-128,127)(0,255)小整数值
SMALLINT2(-32768,32767)(0,65535)大整数值
MEDIUMINT3(-8388608,8388607)(0,16777215)大整数值
INTINTEGER4(-2147483648,2147483647)(0,4294967295)大整数值
BIGINT8(-9223372036854775808,9223372036854775 807)(0,18446744073709551615)极大整数值
FLOAT4(-3.402823466E+38,-1.175494351E-38),0,(1.175494351E-38,3.402823466351E+38)0,(1.175494351E-38,3.402823466E+38)单精度 浮点数值
DOUBLE8(-1.7976931348623157E+308,-2.2250738585072014E-308),0,(2.2250738585072014E-308,1.7976931348623157E+308)0,(2.2250738585072014E-308,1.7976931348623157E+308)双精度 浮点数值
DECIMAL对 DECIMAL(M,D) ,如果 M>D,为 M+2 否则为 D+2依赖于 M 和 D 的值依赖于 M 和 D 的值小数值

日期和时间类型

表示时间值的日期和时间类型为 DATETIMEDATETIMESTAMPTIMEYEAR

每个时间类型有一个有效值范围和一个"零"值,当指定不合法的 MySQL 不能表示的值时使用"零"值。

类型大小 (字节)范围格式用途
DATE31000-01-01/9999-12-31YYYY-MM-DD日期值
TIME3'-838:59:59'/'838:59:59'HH:MM:SS时间值或持续时间
YEAR11901/2155YYYY年份值
DATETIME8'1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'YYYY-MM-DD hh:mm:ss混合日期和时间值
TIMESTAMP4'1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTCYYYY-MM-DD hh:mm:ss混合日期和时间值,时间戳

DATETIME 类型最终展现的形式为:YYYY-MM-DD HH:MM:SS,固定占用 8 个字节。从 MySQL 5.6 版本开始,DATETIME 类型支持毫秒,DATETIME(N) 中的 N 表示毫秒的精度。例如,DATETIME(6) 表示可以存储 6 位的毫秒值。同时,一些日期函数也支持精确到毫秒,例如常见的函数 NOWSYSDATE

TIMESTAMP 类型存储的内容为‘1970-01-01 00:00:00’到现在的毫秒数。TIMESTAMP 占用 4 个字节,因此其存储的时间上限只能到 “2038-01-19 03:14:07”。同类型 DATETIME 一样,从 MySQL 5.6 版本开始,类型 TIMESTAMP 也能支持毫秒。与 DATETIME 不同的是,若带有毫秒时,类型 TIMESTAMP 占用 7 个字节,而 DATETIME 无论是否存储毫秒信息,都占用 8 个字节

字符串类型

字符串类型指 CHARVARCHARBINARYVARBINARYBLOBTEXTENUMSET

类型大小 (字节)用途
CHAR0-255定长字符串
VARCHAR0-65535变长字符串
TINYBLOB0-255不超过 255 个字符的二进制字符串
TINYTEXT0-255短文本字符串
BLOB0-65535二进制形式的长文本数据
TEXT0-65535长文本数据
MEDIUMBLOB0-16777215二进制形式的中等长度文本数据
MEDIUMTEXT0-16777215中等长度文本数据
LONGBLOB0-4294967295二进制形式的极大文本数据
LONGTEXT0-4294967295极大文本数据

说明:

BLOB 是一个二进制大对象,可以容纳可变数量的数据。有 4 种 BLOB 类型:TINYBLOBBLOBMEDIUMBLOBLONGBLOB。它们区别在于可容纳存储范围不同。

有 4 种 TEXT 类型:TINYTEXTTEXTMEDIUMTEXTLONGTEXT。对应的这 4 种 BLOB 类型,可存储的最大长度不同,可根据实际情况选择。

数据类型常见问题

CHAR 和 VARCHAR 的区别是什么?

CHARVARCHAR 的主要区别在于:CHAR 是定长字符串,VARCHAR 是变长字符串。

  • CHAR 在存储时会在右边填充空格以达到指定的长度,检索时会去掉空格;VARCHAR 在存储时需要使用 1 或 2 个额外字节记录字符串的长度,检索时不需要处理。
  • CHAR 更适合存储长度较短或者长度都差不多的字符串,例如 Bcrypt 算法、MD5 算法加密后的密码、身份证号码;VARCHAR 类型适合存储长度不确定或者差异较大的字符串,例如用户昵称、文章标题等。
  • CHAR(M)VARCHAR(M) 的 M 都代表能够保存的字符数的最大值,无论是字母、数字还是中文,每个都只占用一个字符。

BINARYVARBINARY 类似于 CHARVARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值字节的数值值。

金钱相关的数据用什么类型存储?

MySQL 中有 3 种类型可以表示浮点数,分别是 FLOATDOUBLEDECIMAL

采用 FLOATDOUBLE 类型会丢失精度。数据的精确度取决于分配给每种数据类型的存储长度。由于计算机只能存储二进制,所以浮点型数据在存储的时候,必须转化成二进制。

  • 单精度类型 FLOAT 存储空间为 4 字节,即 32 位。
  • 双精度类型 DOUBLE 存储空间为 8 字节,即 64 位。

如果存储的数据转为二进制后,超过存储的位数,数据就被截断,因此存在丢失精度的可能。

更重要的是,从 MySQL 8.0.17 版本开始,当创建表用到类型 Float 或 Double 时,会抛出下面的警告:MySQL 提醒用户不该用上述浮点类型,甚至提醒将在之后版本中废弃浮点类型。

Specifying number of digits for floating point data types is deprecated and will be removed in a future release

【示例】丢失精度案例

-- 创建表
CREATE TABLE `test` (
  `value` FLOAT(10,2) DEFAULT NULL
);

mysql> insert into test value (131072.32);
Query OK, 1 row affected (0.01 sec)

mysql> select * from test;
+-----------+
| value     |
+-----------+
| 131072.31 |
+-----------+
1 row in set (0.02 sec)

说明:示例中,使用 FLOAT 类型,明明保留了两位小数。但是写入的数据却从 131072.32 变成了 131072.31

DECIMAL 类型是 MySQL 官方唯一指定能精确存储的类型。因此,对于不允许丢失精度的场景(如金钱相关的业务),可以使用 DECIMAL 类型。

然而,在海量并发的互联网业务中使用,金额字段的设计并不推荐使用 DECIMAL 类型,而更推荐使用 BIGINT 整型类型。这里会用到一个巧思:将资金类型的数据用分为单位存储,而不是用元为单位存储。如 1 元在数据库中用整型类型 100 存储。

为什么更推荐用 BIGINT 存储金钱数据?因为 DECIMAL 是个变长字段,若要定义金额字段,则定义为 DECIMAL(8,2) 是远远不够的。这样只能表示存储最大值为 999999.99,百万级的资金存储。用户的金额至少要存储百亿的字段,而统计局的 GDP 金额字段则可能达到数十万亿级别。用类型 DECIMAL 定义,不好统一。另外重要的是,类型 DECIMAL 是通过二进制实现的一种编码方式,计算效率远不如整型来的高效。因此,推荐使用 BIGINT 来存储金额相关的字段。

扩展阅读:MySQL 如何选择 float, double, decimalopen in new window

如何存储 emoji 😃?

在表结构设计中,除了将列定义为 CHARVARCHAR 用以存储字符以外,还需要额外定义字符对应的字符集,因为每种字符在不同字符集编码下,对应着不同的二进制值。常见的字符集有 gbkutf8,通常推荐把默认字符集设置为 utf8

随着移动互联网的飞速发展,推荐把 MySQL 的默认字符集设置为 utf8mb4,否则,某些 emoji 表情字符无法在 UTF8 字符集下存储。

【示例】设置表的字符集为 utf8mb4

ALTER TABLE test CHARSET utf8mb4;

注意:上述修改只是将表的字符集修改为 utf8mb4,下次新增列时,若不显式地指定字符集,新列的字符集会变更为 utf8mb4但对于已经存在的列,其默认字符集并不做修改

【示例】设置表的默认字符集为 utf8mb4

正确设置 utf8mb4 字符集方法如下:

ALTER TABLE test CONVERT TO CHARSET utf8mb4;

时间数据选择 DATETIME 还是 TIMESTAMP?

表结构设计时,对时间字段的存储,通常会有 3 种选择:DATETIMETIMESTAMPINT

INT 类型就是直接存储 ‘1970-01-01 00:00:00’ 到现在的毫秒数,本质和 TIMESTAMP 一样,因此用 INT 不如直接使用 TIMESTAMP

TIMESTAMP 类型存储的内容为‘1970-01-01 00:00:00’到现在的毫秒数。TIMESTAMP 占用 4 个字节,因此其存储的时间上限只能到 “2038-01-19 03:14:07”

此外,TIMESTAMP 还存在潜在的性能问题。虽然从毫秒数转换到类型 TIMESTAMP 本身需要的 CPU 指令并不多,这并不会带来直接的性能问题。但是如果使用默认的操作系统时区,则每次通过时区计算时间时,要调用操作系统底层系统函数 __tz_convert(),而这个函数需要额外的加锁操作,以确保这时操作系统时区没有修改。所以,当大规模并发访问时,由于热点资源竞争,会产生两个问题。

  • 性能不如 DATETIME: DATETIME 不存在时区转化问题。
  • 性能抖动: 海量并发时,存在性能抖动问题。

为了优化 TIMESTAMP 的使用,强烈建议使用显式的时区,而不是操作系统时区。比如在配置文件中显示地设置时区,而不要使用系统时区

综上,由于 TIMESTAMP 存在时间上限和潜在性能问题,所以推荐使用 DATETIME 类型来存储时间字段。

小结

  • 自增整型主键,务必使用 BIGINT,而非 INT,以防后期超过数据范围,会很麻烦。

  • 不要再使用浮点类型 FloatDouble,MySQL 后续版本将不再支持上述两种类型。

  • 金钱数据字段,可以使用 DECIMAL 类型。但是更推荐使用 BIGINT,长度固定,存储更紧凑,性能更好。

  • 业务隐私信息,如密码、手机、信用卡等信息,需要加密。切记简单的 MD5 算法是可以进行暴力破解,并不安全,推荐使用动态盐+动态加密算法进行隐私数据的存储。

  • 由于 TIMESTAMP 存在时间上限和潜在性能问题,所以推荐使用 DATETIME 类型来存储时间字段。

参考资料

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7