良好的逻辑设计和物理设计是高性能的基础,应该根据系统将要执行的查询语句来设计schema,这往往需要权衡各种因素。
选择优化的数据类型
Mysql支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。不管存储哪种类型的数据,下面几个简单的原则都有助于做出更好的选择。
- 更小的通常更好
一般情况下,应该尽量使用可以正确存储数据的最小数据类型。
- 简单就好
简单数据类型的操作通常需要更少的CPU周期。
- 尽量避免NULL
很多表都可包含为NULL
(空值)的列,即使应用程序并不需要保存NULL
也是如此,这是因为可为NULL
是列的默认属性。通常情况下最好指定列为NOT NULL
,除非真的需要存储NULL
值。
在为列选择数据类型时,第一步需要确定合适的大类型:数字、字符串、时间等。
下一步是选择具体类型。很多Mysql的数据类型可以存储相同类型的数据,只是存储的长度和范围不一样、允许的精度不同,或者需要的物理空间(磁盘和内存空间)不同。相同大类型的不同子类型数据有时也有一些特殊的行为和属性。
整数类型
有两种类型的数字:整数和实数。如果存储整数,可以使用这几种整数类型:TINYINT
,SMALLINT
,MEDIUMINT
,INT
,BIGINT
。分别使用8,16,24,32,64位存储空间。它们可以存储的值得范围从到,其中N是存储空间的位数。
整数类型有可选的UNSIGNED
属性,表示不允许空值,这大致可以使正数的上限提高一倍。
实数类型
实数是带有小数部分的数字。然而,它们不只是为了存储小数部分;也可以使用DECIMAL
存储比BIGINT
还大的整数。Mysql既支持精确类型,也支持不精确类型。
FLOAT
和DOUBLE
类型支持使用标准的浮点运算进行近似计算。DECIMAL
类型用于存储精确地小数。浮点和DECIMAL
类型都可以指定精度。
浮点类型在存储同样范围的值时,通常比DECIMAL
使用更少的空间。FLOAT
使用4个字节存储。DOUBLE
占用8个字节。
字符串类型
VARCHAR和CHAR类型
VARCHAR
和CHAR
是两种最主要的字符串类型。
关于两种类型的一些比较。
- VARCHAR
VARCHAR
类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型更节省空间,因为它仅使用必要的空间。
**VARCHAR
需要使用1或2个额外字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只使用1个字节表示,否则使用2个字节。**假设使用latin1
字符集,一个VARCHAR(10)
的列需要11个字节的存储空间。VARCHAR(1000)
的列则需要1002个字节,因为需要2个字节存储长度信息。
下面这些情况下使用VARCHAR
是合适的:字符串列的最大长度比平均长度大很多;列的更新更少,所以碎片不是问题;使用了像UTF-8
这样复杂的字符集,每个字符都使用不同的字节数进行存储。
- CHAR
CHAR
类型是定长的:Mysql总是根据定义的字符串长度分配足够的空间。当存储CHAR
值时,Mysql会删除所有的末尾空格。CHAR值会根据需要采用空格进行填充以方便比较。
**CHAR
适合存储很短的字符串,或者所有值都接近同一个长度。对于经常变更的数据,CHAR
也比VARCHAR
更好,因为定长的CHAR
类型不容易产生碎片。对于非常短的列,CHAR
比VARCHAR
在存储空间上也更有效率。**例如用CHAR(1)
来存储只有Y和N的值,如果采用单字节字符集只需要一个字节,但是VARCHAR(1)
却需要两个字节,因为还有一个记录长度的额外字节。
与CHAR
和VARCHAR
类似的类型还有BINARY
和VARBINARY
,它们存储的是二进制字符串。二进制字符串跟常规字符串非常相似,但是二进制字符串存储的是字节码而不是字符。填充也不一样:Mysql填充BINARY
采用的是\0
(零字节)而不是空格,在检索时也不会去掉填充值。
使用
VARCHAR(5)
和VARCHAR(200)
存储‘hello’
的空间开销是一样的。那么使用更短的列有什么优势吗?
事实证明有很大优势。更长的列会消耗更多的内存,因为Mysql通常会分配固定大小的内存块来保存内存值。尤其是使用内存临时表进行排序或操作时会特别糟糕。在利用磁盘临时表进行排序时也同样糟糕。所以最好的策略是只分配真正需要的空间。
BLOB和TEXT类型
BLOB
和TEXT
都是为存储很大的数据而设计的字符串数据类型,分别采用二进制和字符方式存储。
字符类型是TINYTEXT
,SMALLTEXT
,TEXT
,MEDIUMTEXT
,LONGTEXT
;对应的二进制类型是TINYBLOB
,SMALLBLOB
,BLOB
,MEDIUMBLOB
,LONGBLOB
。BLOB
是SMALLBLOB
的同义词,TEXT
是SMALLTEXT
的同义词。
与其他类型不同,Mysql把每个BLOB
和TEXT
值当作一个独立的对象处理。存储引擎在存储时通常会做特殊处理。当BLOB
和TEXT
值太大时,InnoDB
会使用专门的外部存储区域来进行存储,此时每个值在行内需要1~4个字节存储指针,然后在外部存储区域存储实际值。
BLOB
和TEXT
家族之间仅有的不同是BLOB
类型存储的是二进制数据,没有排序规则或字符集,而TEXT
字符有字符集和排序规则。
Mysql不能将BLOB
和TEXT
列全部长度的字符串进行索引,也不能使用这些索引消除排序。
使用枚举(ENUM)代替字符串类型
有时候可以使用枚举列代替常用的字符串类型。枚举列可以把一些不重复的字符串存储成一个预定义的集合。
日期和时间类型
Mysql可以使用许多类型来保存日期和时间值,例如YEAR和DATE。Mysql能存储的最小时间粒度为秒。但是Mysql也可以使用微秒级的粒度进行临时运算。
DATETIME
这个类型能保存大范围的值,从1001年到9999年,精度为秒。它把日期和时间封装到格式为YYYYMMDDHHMMSS
的整数中,与时区无关。使用8个字节的存储空间。
默认情况下,Mysql以一种可排序的、无歧义的格式显示DATETIME
值,例如“2008-01-16 22:37:08”。这是ANSI标准定义的日期和时间表示方法。
TIMESTAMP
TIMESTAMP
类型保存了从1970年1月1日午夜(格林尼治标准时间)以来的秒数,它和UNIX
时间戳相同。TIMESTAMP
只使用4个字节的存储空间,因此它的范围比DATETIME
小得多:只能表示从1970年到2038年。Mysql提供了FROM_UNIXTIME()
函数把Unix
时间戳转换为日期,并提供了UNIX_TIMESTAMP()
函数把日期转换为Unix时间戳。
TIMESTAMP
显示的值依赖于时区。Mysql服务器、操作系统,以及客户端连接都有时区设置。
TIMESTAMP
有DATETIME
没有的特殊属性。默认情况下,如果插入时没有指定第一个TIMESTAMP
列的值,Mysql则设置这个列的值为当前时间。再更新一条记录时,Mysql默认也会更新第一个TIMESTAMP
列的值(除非在UPDATE
语句中明确指定了值)。TIMESTAMP
列默认为NOT NULL
,这也和其他的数据类型不一样。
除了特殊行为外,通常也应该尽量使用TIMESTAMP
,因为它比DATETIME
空间效率更高。
位数据类型
Mysql有少数几种存储类型使用紧凑的位存储数据。所有这些位类型,从技术上来说都是字符串类型。
BIT
可以使用BIT
列在一列中存储一个或多个``true/false值。
BIT(1)定义一个包含单个位的字段,
BIT(2)存储两个位,依此类推。
BIT`列的最大长度是64个位。
应该谨慎使用BIT类型。对于大部分应用,最好避免使用这种类型。
如果想在一个bit的存储空间中存储一个true/false
值,另一个方法是创建一个可以为空的CHAR(0)
列。该列可以保存空值(NULL)或者长度为零的字符串(空字符串)。
SET
如果需要保存很多true/false
值,可以考虑合并这些列到一个SET
数据类型,它在Mysql内部是以一系列打包的位的集合来表示的。
选择标识符
- 整数类型
整数通常是标识列最好的选择,因为它们很快并且可以使用AUTO_INCREMENT
。
- ENUM和SET类型
对于标识列来说,ENUM
和SET
类型通常是一个糟糕的选择。
- 字符串类型
如果可能,应该避免使用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。
如果存储UUID
值,则应该移除“-”符号,或者更好的做法是,用UNHEX()
函数转换UUID
值为16字节的数字,并且存储在一个BINARY(16)
列中。检索时可以通过HEX()
函数来格式化为十六进制格式。
Mysql schema设计中的陷阱
- 太多的列
- 太多的关联(一个粗略的经验法则,如果希望执行得快速其并发性好,单个查询最好在12个表以内做关联)
- 全能的枚举(注意防止过度使用枚举)
- 变相的枚举
- 非此发明的
NULl
(当确实需要表示未知值时也不要害怕使用NULL
)
总结
- 尽量避免过度设计
- 使用小而简单的合适数据类型
- 尽量使用相同的数据类型存储相似或相关的值
- 注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存
- 尽量使用整型定义标识列
- 避免使用Mysql已经遗弃的特性
- 小心使用
ENUM
和SET
- 范式是好的,但是反范式有时也是必需的,并且能带来好处
ALTER TABLE
是让人痛苦的操作,因为在大部分情况下,它都会锁表并且重建整张表