MySQL 合理选择数据类型
介绍
MySQL 支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。合理的选择表字段的数据类型,更有利于数据在磁盘的存储,在系统中占据更小的内存,查询速度更快。对于各种数据类型的存储,可以遵循一些原则,有助于我们做出更好的选择。
选择原则
一. 更小的通常更好
一般情况下,应该尽量使用可以正确存储数据的最小数据类型。更小的数据类型通常更快,因为他们占用更少的磁盘,内存和CPU缓存。并且处理时需要的CPU周期也更少。如果无法确定哪个数据类型最好,就选择不会超过范围的最小值。
二. 简单就好
简单数据类型的操作通常需要更少的CPU周期。例如,整型比字符操作代价更低,因为字符集和校对规则(排序规则) 使得字符比较整型更复杂。比如 应该使用MySQL内建的类型 而不是字符串来存储日期和时间。另外一个应该用整型存储IP地址。
三. 尽量避免NULL
通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。如果查询中包含可为NULL的列,对MySQL来说更难优化,因为可为NULL的列使得索引,索引统计和值比较都更为复杂。可为NULL的列会使用更多的存储空间,在MySQL里也需要特殊处理。当可为NULL 的列被索引时,每个索引记录需要一个额外的字节。所以 当计划在某个列上建索引,就应该尽量避免设计成可为NULL的列。
常规数据类型选择
在为列选择数据类型时,第一步需要确定合适的数据类型:数字,字符串,时间等。下一步是选择具体类型。很多MySQL的数据类型可以存储相同类型的数据,只是存储的长度和范围不一样,允许的精度不同,或者需要的物理空间不同。相同大类型的不同子类型数据有时也有一些特殊的行为和属性。
整数类型
有两种类型的数字:整数和实数。如果存储整数,可以使用这几种整数类型:TINYINT,SMALLINT,MEDIUMINT,INT,BITINT.分别使用8,16,24,32,64位存储空间。它们可以存储的值的范围从 -2 的n-1次方到 2的n-1次方 减一。其中n是存储空间的位数。
整数类型有可选的UNSIGNED属性,表示不允许负值,这样可以使正数的上限提高一倍。例如 TINYINT.UNSIGNED可以存储的范围是0-255。而TINYINT的存储访问是 -128-127.我们的选择决定了MySQL是怎么在内存和磁盘中保存数据的。然而计算一般使用64位的BIGINT整数(一些聚合函数是例外,它们使用DECIMAL或DOUBLE进行计算)
MySQL可以为整数指定宽度,例如INT(11),但这个意义不大,它不会限制值的范围,意味着INT(1)和INT(20)在磁盘上的存储空间是一样的。指定宽度只是规定了客户端显示的字符个数。
实数类型
实数是带有小数部分的数字,它们不仅可以存储小数部分,也可以存储比BITINT还打的整数。MySQL既支持精确计算类型,也支持不精确类型。FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。
DECIMAL类型用于存储精确的小数。因为CPU不支持对DECIMAL的直接计算。所以目前MySQL版本自身实现了DECIMAL的高精度计算,相对而言,CPU直接支持原生浮点计算,所以浮点运算明显更快。
精度
浮点和DECIMAL类型都可以指定精度,对于DECIMAL列,可以指定小数点前后所允许的最大位数,这会影响列的空间消耗。MySQL5.0以后的版本将数字打包打包保存到一个二进制字符串中(每四个字节存储9个数字)。例如DECIMAL(18,9)小数点两边个存储9个数字,一共使用9个字节:小数点前的数字用4个字节,小数点后的数字用4个字节,小数点本身占用1个字节。
浮点类型在存储同样范围的值时,通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储,DOUBLE占用8个字节,相比FLOAT ,DOUBLE有更高的精度和更大的范围。和整型类型一样,能选择的只能是存储类型,MySQL使用DOUBLE作为内部浮点计算的类型。
优化
因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用DECIMAL–例如存储财务数据。但在数据量比较大的时候,可以考虑使用BIGINT代替DECIMAL,将需要存储的货币单位根据小数的位数乘以相应的倍数即可,假设要存储财务数据精确到万分之一,则可以把所有金额乘以1百万,然后将结果存储在BIGINT里,这样可以同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。
字符串类型
VARCHAR和CHAR是两种最重要的字符串类型。不同的存储引擎,它们在磁盘和内存中的存储是不一样的。
VARCHAR
VARCHAR 类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型更节省空间,因为它仅使用必要的空间。VARCHAR 需要使用1或2个额外字节记录字符串的长度。假设采用latin1 字符集,一个VARCHAR(10)列需要11个字节的存储空间。VARCHAR(1000)的列需要1002个字节,需要2个字节存储长度信息。
VARCHAR 节省了存储空间,所以对性能也有帮助。但是由于字段是变长,行也是变长,在UPDATE操作时可能使行变得比原来更长,这就导致需要做额外的工作,如果一个行占用的空间超过了该页的空间,InnoDB则需要分裂页来使行可以放入到页内。
适用场景
1.字符串列的最大长度比平均长度大很多
2.列的更新很少,所以碎片不是问题
3.使用了像UTF-8这样复杂的字符集,每个字符都使用不同的字节数进行存储
5.0后的版本,MySQL 在存储和检索时会保留末尾空格。InnoDB 会将过长的VARCHAR存储为BLOB。
CHAR
CHAR 类型是定长的,MySQL总是根据定义的字符串长度分配足够的空间,当存储CHAR值时,MySQL会删除所有的末尾空格。CHAR 值会根据需要采用空格进行填充以方便比较。
使用场景
1.存储很短的字符串,或者所有值都接近同一个长度。比如 存储密码的MD5值,手机号。
2.对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片
3.对于非常短的列,CHAR也比VARCHAR存储效率更高,因为VARCHAR还需要一个记录长度的额外字节存储。
BINARY与VARBINARY
和CHAR与VARCHAR类似,BINARY和VARBINARY存储的是二进制字符串。二进制字符串存储的是字节码而不是字符,填充也不样。MySQL填充BINARY采用的是\0(零字节)而不是空格,在检索时也不会去掉填充值。
使用场景:
当需要存储二进制数据,并希望MySQL使用字节码而不是字符串进行比较时,这些类型非常有用。二进制比较的优势不仅仅体现在大小写敏感上面,MySQL比较BINARY字符串时,每次按一个字节,并且根据该字节的数值进行比较。因此比较快。
BLOB与TEXT类型
BLOB和TEXT都是为存储很大的数据而设计的字符串数据类型,分别采用二进制和字符串方式存储。与其他类型不同,MySQL把每个BLOB和TEXT值当作一个独立的对象处理。存储引擎在存储时通常会做特殊处理。当BLOB和TEXT值太大时,InnoDB会使用专门的外部存储区域来进行存储,此时每个值在行内需要1-4个字节存储一个指针,然后在外部存储区域存储实际的值。
BLOB和TEXT家族之间仅有的不同是BLOB类型存储的是二进制数据,没有排序规则或字符集,而TEXT类型有字符集和排序规则。
日期和时间类型
MySQL提供两种相似的日期类型:DATETIME和TIMESTAMP.对于很多应用程序,它们都能工作,但是在某些场景,它们会有各自的优势。
DATETIME
这个类型能保存大范围的值,从1001年到9999年,精度为秒。它把日期和时间封装到格式为YYYYMMDDHHMMSS的整数中,与时区无关。使用8个字节进行存储。默认情况下,MySQL以一种可排序的,无歧义的格式显示DATETIME值。
例如 “2023-03-09 12:29:30”。这是ANSI标准定义的日期和时间表示方法。
TIMESTAMP
TIMESTAMP类型保存了从1970年1月1日午夜以来的秒数。它和UNIX时间戳相同。TIMESTAMP只用4个字节存储空间,因此它的范围比DATETIME小的多。只能表示从1970到2038(当时间超过2038,可以采用int类型存储或者DATETIME)。MySQL提供了FROM_UNIXTIME()函数把Unix时间戳转换为日期,并提供了UNIX_TIMESTAMP()函数把日期转换为UNIX时间戳。
TIMESTAMP显示的值也依赖于时区。MySQL服务器,操作系统,以及客户端连接都有时区设置。因此,存储值为0的TIMESTAMP在美国东部时区显示为“1969-12-31 19:00:00”,与格林尼治时间相差5个小时。如果在多个时区存储或访问数据,TIMESTAMP和DATETIME的行为将很不一样。前者提供的值与时区有关系,后者则保留文本表示的日期和时间。
除了特殊行为之外,一般也尽可能使用TIMESTAMP,因为它比DATETIME空间效率更高。
总结
大家常用的字段主要是 整型,数字,字符串和时间。这里对每个类型的选择都做了说明。我们在设计表时如果有不确定的字段类型时,希望本文可以给与参考。谢谢。