高性能mysql总结(一 数据类型优化)
Posted manmanrenshenglu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高性能mysql总结(一 数据类型优化)相关的知识,希望对你有一定的参考价值。
选择优化的数据类型
mysql支持的数据类型非常多,选择正确的数据类型对于获取高性能至关重要。不管存储哪种类型的数据,下面几个简单的原则都有助于做粗活更好的选择。
最小的通常更好
一般情况下,应该尽量使用正确存储数据的最小数据类型。最小的数据类型通常更快,因为它们占用更少的磁盘、内存和cpu缓存,并且处理时需要的cpu周期也更少。
但是要确保没有低估需要存储的值的范围,因为在schema中的多个地方增加数据类型的范围是一个非常耗时和痛苦的操作。如果无法确定哪个数据类型是最好的,就选择你认为不会超过范围的最小类型。
简单就好
简单数据类型的操作通常需要更少的cpu周期。例如,整型比字符操作代价更低,因为字符集和校对规则(排序规则)使得字符比较比整型比较更复杂。这里有两个例子:应该使用mysql内建的类型(date,datetime等)而不是字符串来存储日期和时间,另外一个是应该用整型存储IP地址。
尽量避免NULL
很多表都包含可为NULL的列,即使应用程序并不需要保存NULL也是如此,这是因为可为NULL是列的默认属性。通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。
如果查询中包含可为NULL的值,对于mysql来说更难优化,因为可为NULL的列似的索引、索引统计和值比较更加复杂。可为NULL的列会使用更多的存储空间,在mysql里也需要特殊处理。当可为NULL的列被索引时,每个索引记录需要一个额外的字节,在MyISAM中甚至还可能导致固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。
通常可把NULL的列改为NOT NULL带来的性能提升比较小,所以(调优时)没有必要首先在现有schema中查找并修改掉这种情况,除非确定这会导致问题。但是,如果计划在列上建索引,就应该尽量避免设计成可为NULL的列(本人观点:可为NULL并不是一定不走索引,网上有太多以讹传讹的说法,自己动手测试下就能得出结论)
当然也有例外,例如值得一提的是,InnoDB使用单独的位(bit)储存NULL的值,所以对于稀疏数据(很多值为NULL,只有少量行的列有非NULL值)有很好的空间效率。但这一点并不适用于MyISAM。
mysql的基本数据类型
整数类型
如果存储整数,可以使用这几种整数类型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT。分别使用8,16,24,32,64位存储空间。他们可以存储的值的范围从 -2(N-1) 到 2(N-1)-1 ,其中N是存储空间的位数。
整数类型有可选的UNSIFNED属性,表示不允许负数,这大致可以使正数的上限提高一倍。例如INYINT UNSIGNED可以存储的范围是0~255,而TINYINT的存储范围是-128~127.
有符号和无符号类型使用相同的存储空间,并具有相同的性能,因此可以根据实际情况选择合适的类型。
Mysql可以为整数类型指定宽度,例如INT(11),对大多数应用这是没有意义的:它不会限制值的合法范围,只是规定了Mysql的一些交互工具(例如Mysql命令行客户端)用来显示字符的个数。对于存储和计算来说,INT(1)和INT(20)是相同的。
实数类型
实数是带有小数部分的数字。然而,他们不只是为了储存小数部分;也可以使用DECIMAL存储比BIGINT还大的整数。Mysql既支持精确类型,也支持不精确类型。
FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。如果需要知道浮点运算是怎么计算的,则需要研究所使用的平台的浮点数的具体实现。
DECIMAL类型用于存储精确的小数。在Mysql5.0和更高版本,DECIMAL类型支持精确计算。Mysql4.1以及最早的版本则使用浮点运算来实现DECIMAL的计算。这样做会因为精度损失导致一些奇怪的问题。在这些版本的mysql中,DECIMAL只是一个“储存类型”。
因为cpu不支持对DECIMAL的直接计算,所以在Mysql5.0以及更高的版本中,Mysql服务器自身实现了DECIAML的高精度计算。相对而言,CPU直接支持原生浮点计算,所以浮点运算明显更快。
有很多方法可以指定浮点列所需要的精度,这会使得Mysql悄悄选择不同的数据类型,或者在存储时对值进行取舍。这些精度定义是非标准的,所以我们建议只指定数据类型,不指定精度。
浮点类型在存储同样范围的值时,通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储。DOUBLE使用8个字节,相比FLOAT有更高的精度和更大的范围。和整数类型一样,能选择的只是存储类型;Mysql使用DOUBLE作用内部浮点计算的类型。
因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用DECIMAL——例如存储财务数据。但是在数据量比较大的时候,可以考虑使用BIGINT代替DECIMAL,将需要储存的货币单位根据小数的位数乘以相应的倍数即可。假设要存储财务数据到万分之一分。则可以把所有金额都乘以一百万,然后将结果储存在BIGINT里,这样可以同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。
字符串类型
Mysql支持多种字符串累心个,每种类型还有很多变种。这些类型在4.1和5.0版本发生了很大的变化,使得情况更加复杂。
VARCHAR和CHAR类型是两种最主要的字符串类型。不幸的是,很难精确地解释这些值是怎么存储在磁盘和内存中的,因为这根存储引擎的具体实现有关,下面的描述假设使用的存储引擎是InnoDB或者MyISAM。
VARCHAR
VARCHAR类型用于存储可变长字符串,是最常见的字符串数据类型它比定长类型更节省空间,因为它仅使用必要的空间(例如越短的字符串使用越少的空间)。有一种情况例外,如果Mysql表使用ROW_FORMAT=FIXED创建的话,每一行都会使用定长存储,这会很浪费空间。
VARCHAR使用1或2个额外字节记录字符串的长度:如果列的最大长度小宇或等于255字节,则只使用1个字节表示,否则使用2个字节。假设采用latin1字符集,一个VARCHAR(10)的列需要11个字节的存储空间。VARCHAR(1000)需要1002个字节,因为需要2个字节存储长度信息。
VARCHAR节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要做额外的工作,如果一个行占用的空间增长,并且在页内没有更多的空间可以存储,这种情况下,不同的储存引擎的处理方式是不一样的。例如:MyISAM将会将行拆成不同的片段储存,InnoDB则需要分裂页来使行可以放进页内。
下面这些情况下使用VARCHAR是合适的:字符串列的最大长度比平均长度大很多;列的更新很少,所以碎片不是问题;使用了像UTF-8这样复杂的字符集,每个字符都使用不同的字节数进行存储。
CHAR
CHAR类型是定长的:Mysql总是根据定义的字符串长度分配足够的空间。当存储CHAR值时,Mysql会删除所有的末尾空格。
CHAR适合存储很短的字符串,并且所有值都接近同一个长度。例如,CHAR非常适合存储密码的md5值,因为这是一个定长的值。对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片。对于非常短的列,CHAR比VARCHAR在存储空间上也更有效率。例如用CHAR(1)存储只有Y和N的值,如果采用单字节单字符集只需要一个字节,但是VARCHAR(1)却需要两个字节,因为还有一个记录长度的额外字节。
与CHAR和VARCHAR类似的类型还有BINARY和VARBINARY,他们储存的是二进制字符串。二进制字符串和常规字符串非常相似,但是二进制字符串储存的事字节码而不是字符。
BLOB和TEXT类型
都是为存储很大的数据而涉及的字符串数据类型,非别采用二进制和字符方式存储。
实际上,他们分别属于不同数据类型家族:字符串类型是TINYTEXT,SMALLTEXT,TEXT,MEDUIMTEXT,LONGTEXT;对应的二进制类型是TINYBLOB,SMALLBLOB,BLOB,MEDUIMBLOB,LONGBLOB。BLOB是SMALLBLOB的同义词,TEXT是SMALLTEXT的同义词。
与其他类型不同,Mysql把每个BLOB和TEXT值当作一个独立的对象处理。存储引擎在存储时通常会做特殊处理。当BLOB和TEXT值太大时,InnoDB会使用专门的“外部”储存区域来进行存储,此时每个值在行内需要1~4个字节存储一个指针,然后在内部存储区域存储实际的值。
Mysql不能将BLOB和TEXT列全部长度的字符串进行索引
日期和时间类型
大部分时间类型都没有替代品,因此没有什么是最佳选择的问题。唯一的问题是保存日期和时间的时候需要做什么。Mysql提供两种相似的日期类型:DATETIME和TIMESTAMP。对于很多应用程序,他们都能工作,但是在某些场景,一个比另一个工作的好。
DATETIME
这个类型能保存大范围的值,从1001年到9999年,精度为秒。它把日期和时间封装到格式为YYYYMMDDHHMMSS的整数中,与时区无关。使用8个字节的存储空间。
TIMESTAMP
TIMESTAMP类型保存了从1970年1月1日以来的秒数,它和UNIX时间戳相同。TIMESTAMP只使用4个字节的存储空间,因此它的范围比DATETIME小得多:只能表示从1970年到2038年。
TIMESTAMP显示的值也依赖时区。因此,存储值为0的TIMESTAMP在美国东部时区显示为“1969-12-31 19:00:00”,与格林尼治时间差5个小时。有必要强调一下这个区别:如果多个时区存储或访问数据,TIMESTAMP和DATETIME的行为将很不一样。前者提供的值与时区有关系,后者则保留文本表示的日期和时间。
TIMESTAMP也有DATETIME没有的特殊属性。默认情况下,如果插入时没哟指定第一个TIMESTAMP列的值,Mysql则设置这个列的值为当前时间。在插入一行记录时,Mysql默认也会更新第一个TIMESTAMP列的值(除非在UPDATE语句中明确指定了值)。你可以配置TIMESTAMP列的插入和更新行为。最后,TIMESTAMP列默认为NOT NULL,这也和其他的数据类型不一样。
除了特殊行为之外,通常也应该尽量使用TIMESTAMP,因为它比DATETIME空间效率更高。有时候人们会时间戳存储为整数值,但这不会带来任何收益。用整数保存时间戳的格式通常不方便处理,所以我们不推荐这样做。
如果要存储比秒更小粒度的日期和时间怎么办?Mysql目前没有提供合适的数据类型,但是可以使用自己的存储格式:可以使用BIGINT类型存储微秒级别的时间戳,或者使用DOUBLE存储秒之后的小数部分。这两种方式都可以。
以上是关于高性能mysql总结(一 数据类型优化)的主要内容,如果未能解决你的问题,请参考以下文章