良好的逻辑设计和物理设计是高性能系统的基石,好比反范式设计能够加快某些类型的查询同时也会影响另一些类型的查询效率,因此咱们必须重视Mysql对于数据库的设计(本文主要讲述表字段类型对于数据库性能的影响)。sql
因为Mysql独有的特性和实现细节对性能的影响是很明显的,由于作好Mysql数据库的设计很关键。对于数据库设计,咱们不得不提表字段的类型选择,因为Mysql支持的数据类型很是多,所以如何选择正确的数据类型对于得到高性能相当重要。无论要存储的数据是什么类型,咱们都须要根据一些数据库设计原则来考虑。数据库
更小的一般是更好的(通常状况下,应该尽量使用正确存储数据的最小数据类型。)缓存
为何呢?服务器
(1) 由于更小的数据类型一般更快,由于它们占用更少的磁盘、内存和CPU缓存,而且处理时须要的CPU周期也更短。 (2) 要确保没有低估须要存储的值的范围,更小是相对与数据类型的最大值范围来说的。 (3) 若是没法肯定哪一个数据类型是最好的,就选择你认为不会超过范围的最小类型。
简单就好(简单数据类型的操做一般须要更短的CPU周期。)数据库设计
为何呢?下面有几个例子说明一下缘由。ide
(1) 整型比字符串操做代价更低,由于字符串集和校对规则(排序规则)是的字符比较比整型比较更复杂。 (2) 存储日期和时间应该使用Mysql内建的类型(date,time,datatime)。 (3) IP地址的存储应该用整型(int)。
尽可能避免 NULL (空值)函数
为何呢?工具
(1) 不少表都包含可为NULL的列,就算程序并不须要保存NULL也是如此,这是由于列的默认属性就是可为NULL。一般状况下最好指定列NOT NULL,除非真的须要存储NULL。 (2) 若是查询中包含可为NULL的列,对于Mysql来讲是很难优化的,由于NULL的列使得索引,索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间,在Mysql里也须要特殊处理。当可为NULL的列被索引时,每一个索引记录须要一个额外的字节,在MyISAM里甚至还可能致使固定大小的索引变成可变大小的索引。 (3) 一般把可为NULL的列改成NOTNULL带来性能提高比较小,若是计划在列上建索引的话,就应该尽可能避免设计成可为NULL的列。(也有一个例外,那就是在InnoDB中,会使用单独的位(bit)来存储NULL值,因此对稀疏数据有很好的空间效率。)
总结性能
在为列选择数据类型时,第一步须要肯定合适的大类型(数字、字符串、时间等等),这一般是很简单的,那么下一步就是选择具体的类型了。优化
不少Mysql的数据类型能够存储相同类型的数据,只是存储的长度和范围不同、容许的精度不一样,或者须要的物理空间(磁盘和内存空间)不一样。相同大类型的不一样子类型数据有时候也有一些特殊的行为和属性。
好比:DATATIME 和 TIMESAMP列均可以存储相同类型的数据(时间和日期)而且精确到秒,然而TIMESTAMP只使用DATATIME一半的存储空间,而且会根据时区变化,具备特殊的自动更新能力。另外TIMESTAMP容许的时间范围要小得多,有时候它的特殊能力会成为障碍,这都是咱们开发者须要考虑的。
有两个类型的数字:整数(whole number)和实数(real number)。
若是存储整数,可使用这几种整数类型:TINNYINT(8)、SMALLINT(16)、MEDIUMINT(24)、INT(32)、BIGINT(64)。
整数类型有可选的的UNSIGNED属性,表示不容许为负值,这大体能够是正数的上限提升一倍。
好比:TINYINT UNSIGNED能够存储的范围是0~255,而TINYINT的存储范围是-127~128.
有符号和无符号类型使用相同的存储空间,并具备相同的功能.
所以能够根据实际状况选择合适的类型。
你的选择决定Mysql是怎么在内存和磁盘中保存数据的。
整数通常选择64位的BIGINT整数,即便在32位环境下也是如此。(可是一些聚合函数是例外,它们是使用DECIMAL或DOUBLE进行计算的)
Mysql能够为整数类型指定宽度。
好比:INT(11),对大多数应用这是没有意义的:它不会限制值的合法范围,只是规定了Mysql的一些交互工具(例如Mysql命令行客户端)用来显示字符的个数。对于存储和计算来说,INT(1)和INT(20)是相同的。
一些第三方存储引擎(好比Infobright)有时也有自定义的存储格式和压缩方案,并不必定使用常见的Mysql内置引擎的方式。
实数是带有小数部分的数字。
它们不仅是将来存储小数部分,也可使用DECIMAL存储比BIGINT还要大的整数。Mysql既支持精确类型,也支持不精确类型。
DECIMAL类型用于存储精确的小数。
在Mysql5.0或者更高版本支持精确运算,而在Mysql4.1以及更早版本中使用浮点运算会出现异常(主要是精度的损失致使的)。
FLOAT和DECIMAL类型均可以指定进度。
对于DECIMAL列能够指定小数点先后所容许的最大位数,这会影响列的空间消耗。有不少方法能够指定FLOAT(浮点)列所须要的精度,这会使得Mysql悄悄选择了不一样的数据类型,或者在存储时对值进行取舍,可是这些精度每每都是非标准的,因此通常建议只指定数据类型不指定精度。
因为须要额外的空间和计算开销,因此应该尽可能只在对小数进行精确计算时才使用DECIMAL。
好比存储财务数据,可是若是数据量比较大的时候,能够考虑使用BIGINT代替DECIMAL,将须要存储的货币单位根据小数的位数乘以相应的倍数便可。
FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。
Mysql支持多种字符串类型,每种类型还有不少变种。其中VARCHAR和CHAR是两种最主要的字符串类型。
注意:Mysql存储引擎存储CHAR或者VARCHAR值的方式在内存中和在磁盘上可能不同,因此Mysql服务器从存储引擎读取的值可能须要转换为另一种存储格式。
VARCHAR类型用于存储可变长字符串,是最多见的字符串数据类型。
VARCHAR比定长类型更节省空间,由于它仅使用必要的空间(越短的字符串使用越少的空间)。
VARCHAR须要使用1或2个额外字节记录字符串的长度。
VARCHAR节省了存储空间,因此对性能是有帮助的。
下面是一些VARCHAR适合使用的场景:
(1)字符串列的最大长度比平均长度大不少。
(2)列的更新不多,因此碎片不是问题。
(3)使用了像UTF-8这样复杂的字符集,每一个字符都使用不一样的字节数进行存储。
CHAR类型是定长的。(Mysql老是根据定义的字符串长度分配足够的空间)
CHAR适合存储很短的字符串,或者全部值都接近同一个长度。
和VARCHAR和CHAR相似的类型还有BINARY和VARBINARY,它们存储的都是二进制字符串。
注意:使用VARCAHR(5)和VARCHAR(200)存储“hello”的空间开销都是同样的,那么使用更短的列有什么优点呢?(事实证实有很大的优点)
更长的列会消耗更多的内存,由于Mysql一般会分配固定大小的内存块来保存内部值。尤为是使用内存临时表进行排序或者操做时会特别糟糕。在利用磁盘临时表进行排序时也一样糟糕。
注意:归根到底,最好的策略是只分配真正须要的空间。
BLOB和TEXT都是为存储很大的数据而设计的字符串数据类型,分别使用二进制和字符方式存储。
实际上它们分别属于两组不一样的数据类型家族:
字符串类型有TINYTEXT、SMALLTEXT、TEXT、MEDIUMTEXT、LONGTEXT;
二进制类型有TINYBLOB、SMALLBLOB、BLOB、MEDIUMBLOB、LONGBLOB;
可使用枚举(ENUM)代替字符串类型。不少时候建议使用枚举列代替经常使用的字符串类型。
(1)枚举列能够把一些不重复的字符串存储成一个预约义的集合。
(2)Mysql在存储枚举时很是紧凑,会根据列表值的数量压缩到一到两个字节中。
(3)Mysql在内部会将每一个值在列表中的位置保存为整数,而且在表的.frm文件中保存“数字-字符串”映射关系的“查找表”。
注意:有一个使人吃惊的地方是,枚举字段是按照内部存储的整数而不是定义的字符串进行排序的。
注意:枚举最很差的地方是:字符串列表是固定的,添加或者删除字符串必须使用ALTER TABLE,所以对于一系列将来可能会改变的字符串,使用枚举并非一个好主意,除非接受只能在列表末尾添加元素。
注意:因为Mysql把每一个枚举值保存为整数,而且必须进行查找才能转换为字符串,因此枚举列有一些开销。
Mysql有不少类型能够保存日期和时间值,好比YEAR和DATE。
Mysql能存储的最小时间粒度为秒(MariaDB支持微秒级别的事件类型)。可是Mysql也可使用微秒级别的粒度进行临时运算。
大部分时间类型都没有替代品,所以没有什么是最佳选择的问题。
接下来惟一的问题是保存日期和时间的时候须要作什么。
DATETIME
(1)这个类型能保存大范围的值,从1001年到9999年,精度为秒。
(2)DATETIME把时间和日期封装到格式为YYYYMMDDHHMMSS的整数中,与时区无关。
(3)DATETIME使用8个字节的存储空间。
TIMESTAMP
(1)TIMESTAMP类型保存了从1970年1月1日午夜以来的秒数,它和UNIX时间戳相同。
(2)TIMESTAMP只使用4个字节的存储空间,所以它的范围比DATETIME小得多。
(3)TIMESTAMP显示的值依赖时区。
DATETIME和TIMESTAMP的对比:
(1)默认状况下,若是插入时没有指定第一个TIMESTAMP列的值,Mysql则设置这个列的值为当前时间。(这是DATETIME没有的特性)
(2)在插入一行记录时,Mysql默认也会更新第一个TIMESTAMP列的值。
(3)TIMESTAMP列默认为NOT NULL,这与其余的数据类型不同。
总结
(1)除了特殊行为以外,一般也应该尽量使用TIMESTAMP,由于它比DATETIME空间效率更高。
(2)通常来说不建议把UNIX时间戳保存为整数值,这不会带来任何收益,用整数保存时间戳格式一般不方便处理。
(3)若是需呀存储比秒更小粒度的日期和时间值,可使用BIGINT类型存储微秒级别的时间戳,或者使用DOUBLE存储秒以后的小数部分,也能够用MariaDB替代Mysql。
BIT定义一个包含单个位的字段,BIT(2)存储2个位,最大长度是64个位。
注意:通常建议谨慎使用BIT类型,对于大部分应用来说最好避免使用这种类型。
为identifier(标识列)选择合适的数据类型很是重要。
通常来说更有可能用标识列与其余值进行比较,或者经过标识列寻找其余列。
当选择标识列的类型时,不只仅须要考虑存储类型,还须要考虑Mysql对这种类型怎么执行计算和比较。
一旦选定了一种类型,要确保在全部关联表中都使用一样的类型。
在能够知足值的范围需求,而且预留将来增加空间的前提下,应该选择最小的数据类型。
注意:整数一般是标识列最好的选择,由于它们很快并且可使用AUTO_INCREMENT。
注意:ENUM和SET是最糟糕的选择了;若是可能也尽量避免使用字符串做为标识列,由于它们很消耗空间而且一般比数字类慢。
对于数据库设计,必定要三思然后行,选择最适合的数据列类型还有决定数据列的大小都是很关键的一步。
其实大可没必要惊慌,不管对于任何类型需求的数据表设计,你只要记住一个原则,很重要很重要很重要的原则:尽量使用正确存储数据的最小数据类型。
PS:很晚了,总结先草草了事,往后还会更新......