《高性能MySQL》——schema与数据类型优化

选择优化的数据类型

准则:
更小的通常更好:占用更少的磁盘、内存和CPU,处理时间也更少;
简单就好:比如使用内置日期存储日期而不是字符串,使用整形存储ip地址;
尽量避免NULL:通常会被特殊处理,所以尽量少用(尤其是索引列)。

整数类型

整数:tinyint、smallint、mediumint、int、bigint分别使用8、16、24、32、64位存储空间;可选UNSIGNED属性;置顶宽度int(6)通常是没有意义的:存储不变,交互工具显示用。
实数:decimal(只是存储格式)、float、double;mysql使用double作为内部浮点计算类型;例如财务数据可以乘以倍数使用bigint存储。

字符串类型

varchar和char:varchar只使用必要的存储空间(row_format_fixed除外);varchar需要1-2个字节的长度标识;varchar节省存储空间但是update需要更大开销;
varchar适用:字符串列最大长度比平均长度大很多;列更新少,碎片不是问题;utf8字符集;
char适用:定长、常更新的、非常短的;
注:memory只支持定长行,即使变长字段也是根据最大长度分配最大空间。
binary和varbinary

varchar(5)和varchar(200)虽然存储空间开销一致,但是更长的列会消耗更大的内存,尤其是内存临时表和磁盘临时表。

BLOB和TEXT类型

blob和text分别是二进制和字符串方式存储;
mysql把每个blob和text作为一个独立的对象处理;
blob和text的唯一区别是:blob二进制存储,没有排序规则或字符集,而text有排序规则和字符集;
排序:只对每个列max_sort_length自己排序,可减少配置或者order by substring(column,length);
memory引擎不支持blob和text类型,查询含有这种类型的话会使用磁盘临时表,所以尽量避免使用blob和text类型;
expalin执行计划如果包含using temporary的话,则说明这个查询使用了隐式临时表。

使用枚举(ENUM)代替字符串类型

因为mysql存储时自动转换为紧凑整形存储;
主键关联时,整数比字符串更高效。

日期和时间类型

datetime:1001-9999年,精度是秒,8个字节;
timestamp:1970年以来的秒数,4个字节,最大到2038年;
使用整数带来不了任何效益;
可以使用double和bigint存储毫秒级时间。

位数据类型

技术上来讲都是字符串类型
BIT:容易出现诡异的ascii和字符显示问题;
SET:打包位集合(find_in_set),修改代价高,并且一般来说无法索引查询;也可以使用整数偏移来保存;

选择标识符

关联列尽量使用相同数据类型;尽可能选择最小的数据类型
整数通常是标识列最好的选择;
enum和set适应存储但不适合标识列;
尽量不使用字符串类型作标识列,因为它们很消费空间,而且速度慢;
很随机的数据不适合索引,insert:索引到不同位置;select:逻辑上相邻行会分布在磁盘和内存不同的地方;

性能的噩梦:自动生成的schema,ORM系统。

特殊的数据类型

bigint 存储小数点或毫秒时间;整数存储IP地址。

Mysql schema设置陷阱(只针对mysql)

太多的列:列转换数据结构的代价;
太多的关联:单个查询最好在12个表以内,mysql最大关联数要小于61个;
全能的枚举:防止过度使用,因为修改一次就要执行一次alter table,5.1之前的版本属于阻塞操作;
变相的枚举。

范式和反范式

范式:每一个数据只出现在一个地方,通常表比较多;
反范式:数据出现在多个地方,通常一张表就够了;
优缺点:
> 范式更新操作更快;修改数据较少;表通常比较小,也不需要group by或distinct;
> 范式的缺点是通常需要关联查询,索引无法更有效;
> 反范式查询通常更快。

混用范式和反范式

反范式的应用是复制和缓存,通过触发器实现缓存更新。

缓存表和汇总表

定期重建:不仅是节省资源,而且保存不会有太多碎片,以及完全有顺序的索引。

物化视图

 

计数器表

只是更新一行容易阻塞,所以可以放到100行中,随机更新数据,然后select sum()获取。
更快读的代价是更慢的写。

加快alter table的速度

mysql执行大部分修改表操作是创建空表,然后将数据迁移过去,再删除旧表。
可能不需要重建表的操作:移除auto_increment;增加、移除或更改enum和set常量。
只修改.frm速度是很快的;建空表后交换frm文件也是一个方法;

快速创建myisam索引

先禁止索引,载入数据,开启索引是有效的,仅对唯一索引无效。