前言
在一些小项目,对于性能要求不高的项目,可能不会太去在意数据类型的优化,能用就行,毕竟在小数据量目前这点消耗不大。
但是,需要数据库大的话,千万、亿、十亿...,再往上往往一个很小的优化都能有较大的效果。高性能MySQL中有这么一句话:良好的逻辑设计和物理设计是高性能的基石。
数据类型优化
MySQL支持大量的数据类型,要设计一个优秀的表结构,需要选择正确的数据类型,来获得更高得性能。在数据类型选择时可以参考以下几个原则:
- 最小数据类型:尽量使用可以正常存储数据得最小数据类型,可以用bool不要用int,可以int不用char。数据类型小,磁盘、内存、CPU占用就会小。
- 简单就好:简单的数据类型CPU周期更少,比如日期应该使用mysql内置时间类型,ip地址应该使用int。
- 避免null:可为null的列,会使得索引、索引统计和值比较都变得更加复杂,而且会占用更多的空间。
整数类型
MySQL中一共有以下几种保存整数的类型:
- TINYINT:8位
- SMALLINT:16位
- MEDIUMINT:24位
- INT:32位
- BIGINT:64位
整数类型可以选择UNSIGNED属性,表示不可以是负数。没有属性可以存储的值范围是-2^(N-1) - 2^(N-1)-1,选择了这属性的话就是0 - 2^N-1。有无UNSIGNED属性,使用相同的存储空间,具有相同的性能,根据实际情况进行选择。
注意:MySQL可以位整数类型指定宽度(INT(11)),但是对于大多数应用这是没意义的,并不会限制值的范围或减少占用空间,它只是规定了一些交互工具显示字符的长度。
小数类型
MySQL支持精确类型,也支持不精确类型,在MySQL中一共有以下三种小数类型:
- FLOAT:4字节
- DOUBLE:8字节
- DECIMAL:不定
DECIMAL用于保存精确小数,支持精确计算。相对来说,CPU直接支持原生浮点计算,所以浮点计算速度会更快。
注意:DECIMAL存储同意范围数据时,空间占用较高,计算开销较大。在不需要保证精确的情况下介意使用浮点型,在数据量大的时候,可以考虑使用整数代替。
字符串类型
CHAR与VARCHAR
CHAR与VARCHAR是最重要的两种字符串类型
,但是这两种类型的存储读取和存储引擎相关,所以如果需要深入了解的话,需要参考使用的存储引擎文档。下面基于InnoDB与MyISAM简单记录一下这两种类型的不同:
- VARCHAR:用于存储可变长字符串,相比CHAR定长字符串更加节省空间,但是更新的话需要做一些额外的工作。VARCHAR会使用1-2字节来记录字符串长度。
- CHAR:定长字符串,适合存储较短字符串或者经常修改的字符串。
注意:
- 重要的放第一,尽量给字符串少分配空间。
- 如果ROW_FORMAT被设置成了FIXED的话,VARCHAR也会成为定长字符串。
- InnoDB存储引擎中,可以把过长的VARCHAR存储为BLOB。
- CHAR类型存储时,MySQL会删除末尾空格。
BLOB与TEXT
BLOB与TEXT都是MySQL用来保存很大数据的字符串类型,其中BLOB以二进制方式存储,TEXT以字符方式存储。
BLOB与TEXT其实属于不同数据类型家族,分别为TINYTEXT、SMALLTEXT、TEXT、MEDIUMTEXT、LONGTEXT的TEXT家族,和TINYBLOB、SMALLBLOB、BLOB、MEDIUMBLOB、LONGBLOB的BLOB家族。
BLOB、TEXT类型和其他类型不同,MySQL会将它们作为一个独立对象处理,存储引擎在存储的时候通常也会做一些特殊处理。当值太大的时候,InnoDB会使用外部存储保存数据,内部保存指针。
BLOB与TEXT类型的不同主要就是存储类型不一样,BLOB以二进制保存,没有排序规则和字符集,TEXT以字符串保存,有排序规则和字符集。
这两类型还有以下两点需要注意的地方:
- MySQL的排序,不会对整个字符串进行排序,只会对max_sort_leng配置的长度进行排序。需要的话可以修改max_sort_leng配置的值或者使用ORDER BY SUSTRING(COLUME, LENGTH)来指定排序的长度。
- MySQL不能使用TEXT、BLOB整个字符串来建立索引,也不能使用这些索引消除排序。
ENUM代替字符串
在平时业务中,比如需要保存订单状态的时候,为了节省空间,提高速度,会使用TINYINT来保存。这确实是一个很好的方法,但是MySQL中还有一种类型也可以实现,各有利弊,下面进行简单介绍。
ENUM是MySQL中的枚举类型,MySQL存储ENUM是很紧凑,会根据枚举数量压缩到1-2字节中,MySQL在内部将以整数的形式保存,将整数和枚举值的映射关系保存在表的frm文件中。
枚举类型有以下两点需要特别注意:
- 枚举值的排序是根据对应整数进行的,而不是字符串排序。所以有排序要求的话,需要在设置时与整数相对应,或者使用Field进行排序,但是Field排序无法利用索引消除排序。
- 枚举的字符串列表是固定的,修改特别不方便,除非可以接受只在队尾添加元素。
个人建议:如果不能一开始就确定枚举列表的话,使用TINYINT保存,自己维护映射表较好。
日期和时间类型
MySQL提供了两种类似的数据类型保存时间,分别是DATETIME和TIMESTAMP,大部分场景,这两者都可以很好的工作,只是部分场景可能需要选择一个更加适合的。
注意:MySQL可以保存的最小时间粒度为秒,MariaDB最小时间粒度为毫秒。(MariaDB常用于大数据集群场景)
DATETIME
DATETIME可以保存1001到9999年的时间范围,时间精度为秒。它将时间数据保存在YYYYMMDDHHMMSS的整数中,使用8字节存储空间,未保存时区数据。默认情况下,MySQL以ASNI标准定义显示时间值(2021-05-21 20:40:34)。
TIMESTAMP
TIMESTAMP只使用4字节空间,只能保存1970到2037的时间,时间精度为秒,保存方式与UNIX时间戳相同。MySQL提供了FROM_UNIXTIME函数将时间戳转化为日期,提供了UNIX_TIMESTAMP函数将日期转换为时间戳。TIMESTEAMP保存了时区信息,它的显示数据与时区相关。
其他
除非有特殊需要,不然尽量使用TIMESTAMP,因为它空间占用小,计算速度快。虽然使用整数来保存时间十分常见,但是这没有如何的好处,一般不推荐。
如果需要时间精度需要达到毫秒的话,可以选择使用MariaDB。或者使用刚刚说的不推荐方法,使用BIGINT或者DOUBLE来保存时间数据。
位数据类型
MySQL有少数的数据类型使用位存储数据,这两个应用较少,只简单了解一下。
BIT
bit类型用来在同一列保存多个true/false值,通过指定长度来决定占用位数,最大长度为64.
注意:如果bit(8)中保存了b''00111001',检索时你会得到字符码为57的字符串,也就是字符''9',但是在数字上下文中,得到的将会是数字57。
SET
没怎么看明白,不过简单来说就是,这数据类型不好使!!!