MySQL中Schema与数据类型优化

1 MySQL数据类型

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

1.1 数值类型

类型 大小 范围(有符号) 范围(无符号) 用途
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的值 小数值

1.2 日期和时间类型

类型 大小 ( 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 混合日期和时间值,时间戳

1.3 字符串类型

类型 大小 用途
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 极大文本数据

2 选择优化的数据类型

选择原则:

  • 更小的通常更好。一般情况下,应该尽量使用可以正确存储数据的最小数据类型。更小的数据类型通常更快,因为它们占用更少的磁盘、内存和CPU缓存,且处理时需要的CPU周期也更少。
  • 简单就好。简单数据类型的操作通常需要更少的CPU周期。
  • 尽量避免NULL。通常情况下,最好指定列为NOT NULL,除非真的需要存储NULL值。如果查询中包含可为NULL的列,对MySQL来说更难优化,因为可为NULL的列使得索引、索引统计和值比较都更复杂。

2.1 整数类型

如果存储整数,可以使用这几种整数类型:TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别使用8、16、24、32、64位存储空间。

MySQL可以为整数类型指定宽度,例如INT(10),对于大多数应用来说这是没有意义的:它不会限制值得合法范围,只是规定了MySQL得一些交互工具用来显示字符得个数。对于存储和计算来说,INT(1)和INT(10)是相同的。

整数类型有可选的UNSIGNED属性,表示不允许负值,这大致可以使正数的上限提高1倍。

2.2 实数类型

实数类型使带小数部分的数字。MySQL既支持精确类型,也支持不精确类型。

FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。DECIMAL类型用于存储精确的小数。

浮点类型在存储同样范围的值时,通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储,DOUBLE占用8个字节,相比FLOAT有更高的精度和更大的范围。

因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用DECIMAL——例如存储财务数据。但在数据量比较大的时候,可以考虑使用BIGINT代替DECIMAL,将需要存储的货币单位根据小数的位数乘以相应的倍数即可,这样可以避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。

2.3 字符串类型

VARCHAR

VARCHAR类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型更节省空间,因为它仅使用必要的空间。但是使用ROW_FORMAT=FIXED创建的话,每一行都会使用定长存储,这会很浪费空间。

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

VARCHAR节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要做额外的工作。如果一个行占用的空间增长,并且页内没有更多的空间可以存储,在InnoDB中需要分裂页来使行可以放进页内。

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

CHAR

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

CHAR适合存储很短的字符串,或者所有值都接近同一长度。

BLOB和TEXT类型

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

与其他类型不同,MySQL把每个BLOB和TEXT值当作一个独立的对象处理。存储引擎在存储时通常会做特殊处理。当BLOB和TEXT值太大时,InnoDB会使用专门的“外部”存储区域来进行存储,此时每个值在行内需要1-4个字节存储一个指针,然后再外部存储区域存储实际的值。

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

2.4 日期和时间类型

DATETIME

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

TIMESTAMP

保存的为UNIX时间戳。它只使用4个字节存储空间,因此它的范围比DATETIME小得多:只能表示从给1970到2038年。MySQL提供了FROM_UNIXTIME()函数把Unix时间戳转换为日期,并提供了UNIX_TIMESTAMP()函数把日期转换为Unix时间戳。

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

2.5 选择标识符(identifier)

为标识列(identifier column)选择合适的数据类型非常重要。一般来说,更有可能用标识列与其他值进行比较,或者通过标识列寻找其他列。标识列也可能再另外的表中作为外键使用,所以为标识列选择数据类型时,应选择跟关联表中的对应列一样的类型。

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

3 MySQL schema设计中的陷阱

范式与反范式

在范式化的数据库中,每个事实数据会出现并且只出现一次。相反,在反范式化的数据库中,信息是冗余的,可能会存储在多个地方。

范式的优点和缺点

范式化通常能够带来的好处:

  • 范式的更新操作通常比反范式要快。
  • 当数据较好地方式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。
  • 范式化的表通常更小,可以更好地放在内存里,所以执行操作会更快。
  • 很少有多余数据意味着检索列表数据时更少需要DISTINCT或者GROUP BY语句。

范式化的缺点是通常需要关联。稍微复杂一些的查询语句在符合范式的设计上都可能需要至少一次关联,也许更多。这不但代价昂贵,也可能使一些索引策略无效。

反范式的优点和缺点

反范式化的设计因为所有数据都在一张表中给,可以很好地避免关联。缺点就是有冗余信息,对于更新时的代价较大。

混用范式化和反范式化

实际应用中,一般会结合两者进行混用,使用部分范式化的schema、缓存表,以及其他技巧。

更快地读,更慢地写

为了提升读速度,经常会需要见一些额外索引,增加冗余列,甚至时创建缓存表和汇总表。这些方法会增加写查询的负担,也需要额外的维护任务,但是在设计高性能数据库时,这些都是常见技巧:虽然写操作变得更慢了,但更显著地提高了读操作的性能。

4 总结

良好的schema设计原则是普遍适用的,建议使用一下原则:

  • 尽量避免过渡设计,例如会导致极其复杂查询的schema设计,或者有很多列的表设计。
  • 使用小而简单的合适数据类型,除非真实数据模型中有确切的需要,否则应该尽可能地避免使用NULL值。
  • 尽量使用相同的数据类型存储相似或者相关的值,尤其要在关联条件中使用的列。
  • 注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存。
  • 尽量使用整型定义标识列。
  • 避免使用MySQL已经遗弃的特性,例如指定浮点数的精度。或者整数的显示宽度。

MySQL中Schema与数据类型优化

上一篇:Oracle 数据库操作相关脚本


下一篇:java.sql.SQLException: Region is unavailable-问题及解决