Python实战社群php
Java实战社群程序员
长按识别下方二维码,按需求添加web
扫码关注添加客服数据库
进Python社群▲c#
扫码关注添加客服缓存
进Java社群▲微信
做者丨草捏子
数据库设计
来源丨草捏子(ID:chaycao)工具
最近在学习MySQL优化方面的知识。本文就数据类型和schema方面的优化进行介绍。性能

1. 选择优化的数据类型
MySQL支持的数据类型有不少,而如何选择出正确的数据类型,对于性能是相当重要的。如下几个原则可以帮助肯定数据类型:
更小的一般更好
应尽量使用能够正确存储数据的最小数据类型,够用就好。这样将占用更少的磁盘、内存和缓存,而在处理时也会耗时更少。
简单就好
当两种数据类型都能胜任一个字段的存储工做时,选择简单的那一方,每每是最好的选择。例如整型和字符串,因为整型的操做代价要小于字符,因此当在二者之间选择时,选择整型一般可以得到更好的性能。
尽可能避免NULL
当列可为NULL时,对于MySQL来讲,在索引和值比较等方面须要作更多的工做,虽然对性能的影响不是很大,但也应尽可能避免设计为可为NULL。
除了以上原则,在选择数据类型时,需遵循的步骤:首先肯定合适的大类型,例如数据、字符串、时间等;而后再选择具体的类型。下面将讨论大类型下的一些具体类型,首先是数字,有两种类型:整数和实数。
1.1 整数类型
整数类型和所占用的空间以下:
整数类型 | 空间大小(bit) |
---|---|
TINYINT | 8 |
SMALLINT | 16 |
MEDIUMINT | 24 |
INT | 32 |
BIGINT | 64 |
整数类型所能存储的范围和空间大小有关:-2^(N-1)至2^(N-1)-1,其中N为空间大小的位数。
整数类型具备UNSIGNED的可选属性,当声明时,表示不容许负数,则存储范围变为:0至2^(N)-1,扩大了一倍。
在MySQL中,还能够为整数类型指定宽度,例如INT(1),但这样的意义并不大,并不会限制值的合法范围,仍能存储-2^31至2^31-1的值,所影响的是与MySQL的交互工具显示字符的个数。
1.2 实数类型
实数类型的对好比下:
实数类型 | 空间大小(Byte) | 取值范围 | 计算精度 |
---|---|---|---|
FLOAT | 4 | 负数:-3.4E+38~-1.17E-38;非负数:0、1.17E-38~3.4E+38 | 近似计算 |
DOUBLE | 8 | 负数:-1.79E+308~-2.22E-308;非负数:0、2.22E-308~1.79E+308 | 近似计算 |
DECIMAL | 与精度有关 | 同DOUBLE | 精确计算 |
从上面能够看出,FLOAT和DOUBLE都有固定的空间大小,但同时因为是使用标准的浮点运算,因此只能近似计算。而DECIMAL则能够实现精确计算,与此同时占用的空间会相较更大,所耗费的计算开销也更多。
DECIMAL所占空间大小与指定的精度有关,例如DECIMAL(M,D):
M为整个数字的最大长度,取值范围为[1, 65],默认值为10;
D为小数点后的长度,取值范围为[0, 30],且D <= M,默认值为0。
MySQL在存储DECIMAL类型时会做为二进制字符串存储,每4个字节存9个数字,当不足9位时,数字的占用空间以下:
数字个数 | 占用空间(Byte) |
---|---|
一、2 | 1 |
三、4 | 2 |
五、6 | 3 |
七、8 | 4 |
小数点先后将分别存储,同时小数点也要占1个字节。下面举两个计算的例子:
DECIMAL(18, 9):整数部分长度为9,占用4个字节。小数部分长度为9,占用4个字节。同时加上小数点1个字节,则总共占用9个字节。
DECIMAL(20, 9):整数部分长度为14,占用7(4+3)个字节。小数部分长度为9,占用4个字节。同时加上小数点1个字节,则总共占用12个字节。
能够看出DECIMAL的空间占用仍是很大的,所以只有当须要对小数进行精确计算时,才须要使用DECIMAL。除此以外,咱们还可使用BIGINT代替DECIMAL,例如须要保证小数点后5位的计算,能够将值乘上10的5次方后做为BIGINT存储,这样能同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。
1.3 字符串类型
最经常使用的字符串类型当属VARCHAR和CHAR。VARCHAR做为可变长字符串,会使用1或2个额外字节记录字符串的长度,当最大长度未超过255时,只需1个字节记录长度,超过255,则需2个字节。VARCHAR的适用场景:
最大长度比平均长度大不少;
列的更新少,避免碎片;
使用复杂的字符集,如UTF-8,每一个字符能使用不一样的字节存储。
CHAR则为定长字符串,根据定义的字符串长度分配足够的空间,适用场景:
长度短;
长度相近,例如MD5;
常常更新。
除了VARCHAR和CHAR,针对存储大字符串,可使用BLOB和TEXT类型。BLOB和TEXT的区别在于,BLOB是以二进制方式存储,而TEXT是以字符方式存储。这也致使,BLOB类型的数据没有字符集的概念,没法按字符排序,而TEXT类型则有字符集的概念,能够按字符排序。二者的使用场景,也由存储格式决定了,当存储二进制数据时,例如图片,应使用BLOB,而存储文本时,例如文章,则应使用TEXT类型。
1.4 日期和时间类型
MySQL中所能存储的最小时间粒度为秒,经常使用的日期类型有DATETIME和TIMESTAMP。
类型 | 存储内容 | 空间大小(Byte) | 时区概念 |
---|---|---|---|
DATETIME | 格式为YYYYMMDDHHMMSS的整数 | 8 | 无 |
TIMESTAMP | 从1970年1月1日零点以来的秒数 | 4 | 有 |
TIMESTAMP显示的值将依赖于时区,意味在不一样时区查询到的值将不同。除了以上列出的不一样,TIMESTAMP还具备一个特殊属性,在插入和更新时,若是没有指定第一个TIMESTAMP列的值,将会设置这个列的值为当前时间。
咱们在开发过程当中,应尽可能使用TIMESTAMP,主要是由于其空间大小仅需DATETIME的一半,空间效率更高。
若是咱们想存储的日期和时间精确到秒以后,怎么办?因为MySQL并未提供,因此咱们可使用BIGINT存储微妙级别的时间戳,或者使用DOUBLE存储秒以后的小数部分。
1.5 选择标识符
一般来讲整数是标识符的最好选择,主要是由于其简单,计算快,且可以使用AUTO_INCREMENT。
2. 范式和反范式
简单来讲,范式就是一张数据表的表结构所符合的某种设计标准的级别。第一范式,属性不可分割,如今的RDBMS系统建成的表都是符合第一范式的。而第二范式,则是消除非主属性对码(能够理解为主键)的部分依赖。第三范式消除非主属性对码的传递依赖。具体的介绍,能够读读知乎上的这个回答(https://www.zhihu.com/question/24696366/answer/29189700)
严格范式化的数据库中,每一个事实数据会出现且只出现一次,不会出现数据冗余,这样所能带能带来的好处有:
更新操做更快;
修改更少的数据;
表更小,更好地放内存中,执行操做更快;
更少须要DISTINCT或GROUP BY。
但也因为数据分散存在各张表中,查询时须要对表进行关联。而反范式的优势则是不用进行关联,将数据冗余存储。
在实际应用中,不会出现彻底的范式化或彻底的反范式化,时常须要混用范式和反范式,使用部分范式化的schema,每每是最好的选择。关于数据库设计,在网上看到这样一段话,你们能够感觉下。
数据库设计应该分为三个境界:
第一境界:刚入门数据库设计,范式的重要性还未深入理解。这时候出现的反范式设计,通常会出问题。
第二境界:随着遇到问题解决问题,渐渐了解到范式的真正好处,从而能快速设计出低冗余、高效率的数据库。
第三境界:再通过N年的锻炼,是必定会发觉范式的局限性的。此时再去打破范式,设计更合理的反范式部分。
范式就像武侠里面的招数,初学者妄想不按招数来,只能死的很难堪。毕竟招数都是高手总结概括的精华。而随着武功提升,招数熟练以后,必然是发现招数的局限性,要么忘掉招数,要么自创招数。
只要努力,加上多熬几年,总能达到第二个境界,总会以为范式是经典。此时能不过度依赖范式,快速突破范式局限性的人,天然是高手。
4. 缓存表和汇总表
除了上述说到的反范式,在表中存储冗余数据,咱们还能够建立一张彻底独立的汇总表或缓存表,来知足检索的须要。
缓存表,指的是存储能够从schema其余表中获取数据的表,也就是逻辑上冗余的数据。而汇总表,则指的是存储使用GROUP BY等语句聚合数据,计算出的不冗余的数据。
缓存表,可用于优化搜索和检索查询语句,这里可使用的技巧有对缓存表使用不一样的存储引擎,例如主表使用InnoDB,而缓存表则可以使用MyISAM,得到更小的索引占用空间。甚至能够将缓存表放到专门的搜索系统中,例如Lucene。
汇总表,则是为了避免实时计算统计值所带来的高昂代价,代价来自两方面,一是须要扫描表中的大部分数据,二是创建特定的索引,会对UPDATE操做有影响。例如,查询微信过去24小时的朋友圈数量,则可固定每1小时扫描全表,统计后写一条记录到汇总表,当查询时,只需查询汇总表上最新的24条记录,而没必要每次查询时都去扫描全表进行统计。
在使用缓存表和汇总表时,必须决定是实时维护数据仍是按期重建,这取决于咱们的需求。按期重建相比实时维护,能节省更多的资源,表的碎片更少。而在重建时,咱们仍需保证数据在操做时可用,须要经过“影子表”来实现。在真实表后建立一张影子表,当填充好数据后,经过原子的重命名操做来切换影子表和原表。
5. 加快ALTER TABLE操做的速度
当MySQL在执行ALTER TABLE操做时,每每是新建一张表,而后把数据从旧表查出并插入到新表中,再删除旧表,若是表很大,这样须要花费很长时间,且会致使MySQL的服务中断。为了不服务中断,一般可使用两种技巧:
在一台不提供服务的机器上执行ALTER TABLE操做,而后再与提供服务的主库进行切换;
“影子拷贝”,创建一张与原表无关的新表,在数据迁移完成后,经过重命名操做进行切换。
但也不是全部的ALTER TABLE操做会引发表重建,例如在修改字段的默认值时,使用MODIFY COLUMN会进行表重建,而使用ALTER COLUMN则不会进行表重建,操做速度很快。这是由于ALTER COLUMN在修改默认值时,会直接修改了存在表的.frm文件(存储字段的默认值),而并未重建表。
参考
《高性能MySQL》
MySQL DECIMAL 数据类型
(https://my.oschina.net/u/559356/blog/3057960)
程序员专栏 扫码关注填加客服 长按识别下方二维码进群
近期精彩内容推荐:
在看点这里好文分享给更多人↓↓