由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会使每个page中可存放的数据行数增大,那么每次 IO 可访问的行数也就增多了。反过来说,处理相同行数的数据,需要访问的 page 就会减少,也就是 IO 操作次数降低,直接提升性能。此外,由于我们的内存是有限的,增加每个page中存放的数据行数,就等于增加每个内存块的缓存数据量,同时还会提升内存换中数据命中的几率,也就是缓存命中率。
?
选择优化的数据类型
?
MySQL支持很多种不同的数据类型,并且选择正确的数据类型对于获得高性能至关重要。不管选择何种类型,下面的简单原则都会有助于做出更好的选择:
?
1、越简单越小越好
?
更小的数据类型通常更快,因为它们使用了更少的磁盘空间、内存和CPU缓存,而且需要的CPU周期也更少。越简单的数据类型,需要的CPU周期就越少。例如:比较整数的代价小于比较字符,因为字符集和排序规则使字符比较更复杂。
?
2、避免空(NULL)
?
要尽可地把字段定义为NOT NULL ,如果计划对列进行索引,就要尽量避免把它设置为可为空(NULL),NULL 类型比较特殊,SQL 难优化。虽然 MySQL NULL类型和 Oracle 的NULL 有差异,会进入索引中,但如果是一个组合索引,那么这个NULL 类型的字段会极大影响整个索引的效率。此外,NULL 在索引中的处理也是特殊的,也会占用额外的存放空间。很多人觉得 NULL 会节省一些空间,所以尽量让NULL来达到节省IO的目的,但是大部分时候这会适得其反,虽然空间上可能确实有一定节省,倒是带来了很多其他的优化问题,不但没有将IO量省下来,反而加大了SQL的IO量。所以尽量确保 DEFAULT 值NOT NULL,也是一个很好的表结构设计优化习惯。
?
难以优化了使用了可空列的查询,它会使索引、索引统计和值更加复杂
?
可空列需要更多的存储空间,还需要在内部进行特殊处理
?
当可空列被索引的时候,每条记录都需要一个额外的字节,还能导致MyISAM中固定大小的索引(例如:一个整数列上的索引)变成可变大小的索引
?
把NULL列改为NOT NULL 带来的性能提升很小,所以除非确定它引入了问题,否则就不要把它当成优先的优化措施
?
即使要在表中存储可为空的字段,也是有办法不使用NULL的,可以考虑使用0,特殊值或字符串来代替它
?
3、尽可能不要直接 SELECT * 读取全部字段,尤其是表中存在 TEXT/BLOB 大列的时候。可能本来不需要读取这些列,但因为偷懒写成 SELECT * 导致内存buffer pool被这些“垃圾”数据把真正需要缓冲起来的热点数据给洗出去了
?
4、超过20个长度的字符串列,最好创建前缀索引而非整列索引(例如:ALTER TABLE t1 ADD INDEX(user(20))),可以有效提高索引利用率,不过它的缺点是对这个列排序时用不到前缀索引。前缀索引的长度可以基于对该字段的统计得出,一般略大于平均长度一点就可以了
?
5、定期用 pt-duplicate-key-checker 工具检查并删除重复的索引。比如 index idx1(a, b) 索引已经涵盖了 index idx2(a),就可以删除 idx2 索引了
?
6、有多字段联合索引时,WHERE中过滤条件的字段顺序无需和索引一致,但如果有排序、分组则就必须一致了
?
7、默认地,使用InnoDB引擎,InnoDB必须要有自增(或类似自增)属性的主键
?
8、若无特殊需求,均可使用latin1字符集,否则用utf8\utf8mb4等大字符集保证通用性
?
9、基数小的不适合建立索引,复合索引比普通索引更合适
?
数据库类型
?
"TINYINT"=>1,
"SMALLINT"=>2,
"MEDIUMINT"=>3,
"INT"=>4,
"BIGINT"=>8,
"FLOAT"=>'if ($M <= 24) {return 4;} else {return 8;}',
"DOUBLE"=>8,
"DECIMAL"=>'if ($M < $D) {return $D + 2;} elsif ($D > 0) {return $M + 2;} else {return $M + 1;}',
"NUMERIC"=>'if ($M < $D) {return $D + 2;} elsif ($D > 0) {return $M + 2;} else {return $M + 1;}',
"DATE"=>3,
"DATETIME"=>8,
"TIMESTAMP"=>4,
"TIME"=>3,
"YEAR"=>1,
"CHAR"=>'$M',
"VARCHAR"=>'$M+1',
"TINYBLOB"=>'$M+1',
"TINYTEXT"=>'$M+1',
"BLOB"=>'$M+2',
"TEXT"=>'$M+2',
"MEDIUMBLOB"=>'$M+3',
"MEDIUMTEXT"=>'$M+3',
"LONGBLOB"=>'$M+4',
"LONGTEXT"=>'$M+4'
?
1、数字类型
整数
?
如果存储整数,就可以使用这几种整数类型,如下所示
还有一个,BIT(M) approximately (M+7)/8 bytes
?
SIGNED 和 UNSIGNED 占用的存储空间是一样的,性能也一样,如果确定没有负数,那就是采用UNSIGNED吧,比如作为主键的ID
?
整数运算通常使用64位的BINGINT整数
?
你可以对整数类型定义宽度,比如INT(11),这对于大在多数应用程序是没有意义的,它不限制值的范围,只规定了的交互工具(例如命令客户端)用来显示字符的个数,对于存储计算,INT(1)和INT(20)是一样的
?
用INT UNSIGNED 存储IPV4地址,用INET_ATON()、INET_NTOA()进行转换,基本上没必要使用CHAR(15)来存储,或者使用程序转换之后存入数据库,因为IP地址本身就是32大小位数字
?
实数
?
实数有分数部分,然而,它们并不仅仅是分数,可以使用DECIMAL保存比出BIGINT还大的整数
?
同时支持精确与非精确类型
?
FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算
?
比较起DECIMAL类型,浮点类型保存同样大小的值使用的空间通常更小,而且精度更大,范围更广,和整数一样,你选择的仅仅是存储类型
?
MYSQL在内部对浮点类型使用DOUBLE进行计算
?
由于需要额外的空间和计算开销,只有在需要对小数进行精确的时候才使用DECIMAL,比如保存金融数据
?
一般不要使用DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。同样,固定精度的小数,也不建议使用DECIMAL,建议乘以固定倍数转换成整数存储,可以大大节省存储空间,且不会带来任何附加维护成本
?
2、字符串类型
VARCHAR和CHAR
?
varchar:保存了可变长度的字符串,是使用得最多的字符串类型,它能比固定类型占用更少的存储空间,因为它只