前言:
优秀的设计和逻辑是高性能的基本保障,我们来看下关于mysql使用schema和数据类型优化的一些建议和原则。
1.选择优化的数据类型的常用规则
- 更小的通常更好
一般选择更小的数据类型能满足业务场景的需求,小的数据类型意味着更小的cpu开销,更小的内存,储存空间的使用,可以提高效率。 - 简单的数据类型
整型的数据类型比字符型,浮点型数据要来的简单,处理起来也更方便。 - 尽量避免使用NULL
如果不是必须要储存NULL值的话,一般情况下都要指定列为NOT NULL。可以为NULL的列会使用更多的空间,使用的时候也需要消耗额外的资源。
而且当列时NULL的时候,建立索引也需要额外的开销,并且上面的唯一索引不会将NULL算为唯一的,多条NULL的数据都可以保存成功,会对数据的唯一性也造成干扰。
2. 选择数据类型的优化
2.1 整数类型 :
1.mysql的证书类型:TINYINT(8),SMALLINT(16),MEDIUMINT(24),INT(32),BIGINT(64)后面小括号中的数字是占用的存储空间。
2.unsigned不会影响mysql的储存空间,有符号的INTYINT储存范围是-128~127,无符号的TINY能储存0~255。总共包含的数值尺寸(总共256个数字),
但是数据的范围是有变化的。可以根据具体的场景来设置最合适的范围。
3.对于整数类型而言int(1)和int(20)没有区别,计算和储存都不会有影响,只是可能会影响一些mysql的客户端工具显示字符的个数。
注:这里稍微扯一点题外话,TINYINT设置为1和大于1的数字,会影响mybatis generator逆向生成java bean和xml的时候的数据格式。
TINYINT(1)会对应Boolean类型,TINYINT(n) n>1的话显示的是Byte类型。
2.2 实数类型
1.mysql内部使用DOUBLE作为浮点型运算的类型,DECIMAL只是一种储存格式,在计算的时候也会转换为DOUBLE。DOUBLE和DECIMAL,mysql5.0之后对DECIMAL进行了优化,可以支持高精度的计算,但是会比较消耗性能,DOUBLE计算更快但是可能会丢失经度。
2.因为DECIMAL使用了更多的空间和计算的消耗,所以只在对小数进行精确计算的时候才使用DECIMAL,正常储存浮点数不用计算的话,DOUBLE就够了。
3.还有一种方式就是使用整型来储存浮点数,将要储存的浮点数乘以小数的位数变成整型在储存起来。这样可以避免浮点储存计算不精确和DECIMAL计算消耗大的问题。
2.3 字符串类型
1.varchar可能是我们用的最多的类型了,它是可变长度的,varchar(1000)如果只用到了10个字节,实际上储存空间上就用了11个字节,一个字节用来记录字符串的长度,当字符串超过255个字节的时候会使用两个字节。另一个char是不可变的字符串,
如果是char(10),实际上使用了11个字节,1个用来记录字符串的长度。
2.不过因为varchar是可变长的,如果字段更新操作时变长了,会需要额外的开销,所以varchar最合适的场景是字符串最大长度比平均长度大很多,而且更新不频繁。
3.char在储存字符串的时候,会把末尾的空格给去掉,如果是'abc ',实际使用和储存时都是'abc'。
4.如果设置了ROW_FORMAT=FIXED,即使是varchar也是会使用定长字节来储存的
5.之前从来没有用过mysql的枚举类型数据,看了书才知道还有枚举类型,平时基本上都是用的java建的枚举类使用比较方便。mysql的枚举类底层储存的也是整数,估计是做了
hash的操作给映射成整数了,不过如果mysql的枚举要联表查询的话性能消耗比较高,可能这也是不太常用的原因之一吧。
2.4 日期类型 官方文档链接。
1.datetime格式范围大概是从1001年-9999年(如果超过了9999年会怎么样呢?会报错的),timestamp这个是从1970年1月1日开始的,而且范围贼小到2038-01-19 03:14:07,
因为我们是东八区所以可以在加八个小时。
所以最好不要用时间戳,这还有18年可能你写的程序就会因为时间格式超过了范围而报错。
2.timestamp在插入时没有值会默认写入一个当前时间戳的值,切不能为null。
3.mysql数据库支持的时间粒度是秒,如果要储存更小粒度的时间,可以使用mysql的分支MariaDB,或者使用BIGINT,DOUBLE处理后来储存时间戳。
2.5 位数据类型
1.set和bit都可以用来存储true/false的值,不是很常用。例如保存某个数据的状态可以用bit,不过一般用tinyint会更常见一些。
2.6 选择标识符
1.为标识列选择合适的数据类型很重要。整数型 > 字符串 > set/enum
2.7 特殊类型数据
1.这里谈到一个例子IPV4,人们经常用VARCHAR(15)来储存,实际上去除掉符号点,是一个无符号的整数。用整数来储存更加合适,
mysql提供了INET_ATON()和INET_NOTA()来转换。
3.schema设计的陷阱
3.1 太多的列
mysql储存引擎和服务层之前拷贝数据的时候,会将缓存的内容解码为各个对应的列。但是
一张表如果列宽很长,几千个列这样,实际使用的时候有时候只用到了小部分数据,这样解码效率就很低,查询也会变得很慢。
3.2 太多的关联
mysql限制了每个关联最多61张表(当然我觉得是够用了,要是一个关联能关联61张表,那就设计上很有问题了)。经验法则,一个查询要想又快又能保持并发性良好,关联最好不超过12张表。
3.3 全能的枚举
一个枚举如果包含了太多的数据,当成了字典来使用的话,当要进行修改的使用因为是阻塞操作,会导致使用这个枚举的业务都停下来。
3.4 变相的枚举
当使用set来代替enum来使用的时候,比如记录是true/false,如果有很多个这样的值,用set来储存容易导致混乱,最好用enum。
3.5 NULL的替代方案
前面说了尽量不使用NULL,我们可以使用-1,0,空字符串来代替。但是不要每个地方都这样用,有时候使用替代方案可能会导致编写代码和业务场景变得更加复杂,比如其他部门都用NULL,我们使用-1,这样很多调用部分都得做出相应的修改。
在具体的场景可以具体分析,并不是说NULL就不能用了。