表结构优化是MySQL性能优化中的重要一环,性能优化从设计阶段就应该被考虑,良好的表结构设计从一开始就为系统的高性能打下了基础。本文整理了MySQL表结构优化的一些原则、经验和技巧。
一、选择合适的存储引擎
MySQL存储引擎建议使用InnoDB,支持事务,支持行级锁,数据更安全。MySQL的其他引擎在实际应用中并不多,比如曾经风光无限的MyISAM引擎随着时代的发展逐渐没落,MySQL组复制Group Replication 只支持InnoDB存储,MySQL 8.0 元数据管理也只使用InnoDB,不再使用MyISAM。
二、选择合适的数据类型
- 表中要有主键,主键字段越小越好,主键最好使用自增的整型。由于在二级索引中会存储主键的值,太大的主键会导致磁盘占用过大,占用太多的内存buffer pool,降低查询效率。不要使用md5,uuid这种类型的数据做主键,这类无序主键会会导致页频繁分裂,影响写入性能,造成数据空洞,占用过多磁盘空间。
- 数据类型越小越好,前提是满足业务需求。越小的数据类型运算更快,占用磁盘空间也更小。
- 能用固定长度的就不要使用变长类型,固定长度的类型容易被缓存,处理性能也更高。
- 使用简单的数据类型,能使用数值型的,就不要使用字符串,数值计算的效率远高于字符串。比如IP地址使用整型来存储比使用字符串要更快,更少占空间。
- 有限值字段使用enum类型,enum占用空间更少,效率更高,比如性能、民族、国家等有限固定的数据,建议使用enum。
- 对于小数类型,尽量不使用float,double,这两个类型存在精度丢失的问题。固定精度的小数也不建议使用decimal,建议乘以固定倍数转换成整数来存储,可以节省存储空间,提高查询效率。
- 对于整数类型,如果确定值不会有负数,定义字段类型时加上unsigned。
- 字段尽可能使用not null,尤其对于需要索引的字段,虽然null值可以节省空间,但会带来很多优化问题,另外null值在很多时候容易引起误解,比如select count(name),如果name为null,获取的总数可能并不如预期。
- 字符串尽量少用text、blob等大文本类型,定长类型用char,不定长类型用varchar。
三、表的范式和反范式设计
- 范式化设计可以减少数据冗余,表通常更小,更新操作更快,但是在查询时需要将多个表进行关联,索引优化比较复杂。
- 反范式化设计可以减少查询时的表关联,可以更好的优化索引,但是存在数据冗余及多份数据的维护异常,对数据修改需要更多的成本。
范式化和反范式化设计没有绝对的好与坏,但需要遵循一定的原则。设计表结构时,以范式化设计为主,在优化表结构阶段,可允许一定的反范式设计,以减少不必要的表关联,提高某些特定场景下的查询效率。
四、表的垂直拆分
当一个表有很多字段时,需要考虑是否把表拆小一点,解决表宽度过大的问题。垂直拆分通常基于如下原则:
- 常用的字段与不常用的字段分开,把不常用的字段拆到单独的一个表中。
- 把text,blob等大字段单独拆到一个表中。
五、表的水平拆分
MySQL单表数据量过大时,会严重影响查询性能,此时就需要考虑对表进行水平拆分,也就是分库分表,所有子表的数据结构完全一致,根据不同的分库分表算法进行拆分。目前也有很多数据库中间件实现了分库分表的功能,比如mycat, atlas,cetus等等,使用数据库分库分表中间件,能够做到业务无感知,就像是使用一张表一样。