高性能mysql-数据类型与表结构优化(一)

来自于高性能mysql的一部分总结。

1.schema和数据类型优化

选择数据类型的原则:
1. 小。
小,占磁盘小,占内存小,占cpu少。
比如varchar字段,varchar(10)和varchar(255),存储“a”的时候,虽然都占两个字节,但是在从磁盘读取到内存的时候,内存却要准备255字符对应的大小的内存块,比如排序操作,或者临时表的时候。因为内存并不知道varchar字段到底真正存了一个多大的数据,所以只能最坏打算。
2. 简单
整型比字符串简单,字符串有字符集、校对规则。
使用日期类型而不是字符串。
使用整形存储ip,而不是字符串。
3. 避免null
null需要更多存储空间,需要多一个字节标记是否为null。
mysql对null需要特殊的处理。

1.1 数据类型

1.1.1 整型类型

tinyint、smallint、mediumint、int、bigint,分别占8、16、24、36、64位存储空间。
对于int(11)这样规定大小是无意义的,因为int不管规定多大的大小,都不会影响其占的字节数以及大小范围,只会对一些mysql交互工具显示产生影响。

1.1.2 浮点型

Decimal除了存储小数之外,可以存储比bigint还大的数。

float、double和decimal都可以表示小数,但是float和double使用的是标准的浮点运算,cpu直接使用原声的浮点运算,所以可能会导致结果的不精确。(比如java,double a = 1.0f - 0.1f = 0.8999999这种现象)。但是decimal是精确计算,mysql服务器自身实现了对decimal的精确计算处理。decimal占用空间大,decimal(18,9),整数位占4个字节,小数位占4个字节,小数点占1个字节。

1.1.3 字符串类型

varchar 比char更节省空间,除非row_format=fix定长控制(几乎不会)。
varchar需要1到2个字节,记录字符串长度,小于255长度,用1个字节记录,大于,用2个字节记录。
varchar是变长的,所以update操作的时候,可能导致页分裂
varchar(255)和varchar(10)存储“a”这个字符,磁盘上占用的空间是一样的,但是当读取到内存,进行排序或者生成临时表的时候,占用的空间是不一样的,内存对于varchar(255)不知道其实际存储了多大的字符,所以会按最坏的情况,分配255个空间,所以为了节省cpu和内存,varchar需要尽可能小。

char存储,会把末尾空格删掉,比较的时候会填充空格进行比较(这不分存储引擎,因为这个操作是mysql服务层做的),同样大小的字符,char比varchar节省空间,因为varchar需要1或2个字节记录字符串长度。

还有一个和char和varchar性质类似的是,binary和varbinary,binary存储的是二进制字符串。

对于随机字符串,比如md5()或uuid(),因为大小随机,所以,插入位置是随机的,会造成随机访问,页分裂、聚簇索引碎片等,所以insert会慢,select 也会慢,因为逻辑位置相邻的数据物理位置不同,局部性原理失效(局部性原理是说,磁盘预读几页数据缓存起来,局部性原理认为,访问这个位置的数据,很可能会访问这个位置附近的数据)。
对于uuid这种随机字符串,可以使用unhex()函数转成16字节的数字存储,检索的时候再hex()转换回来。

blob和text类型
因为内存不知道其实际存储了多少个字符,所以极其好性能,会使用磁盘临时表,可以使用substring()截取成字符串,来使用内存临时表。
当然,这种字段,尽可能不用。

1.1.4 枚举

枚举排序,使用的枚举对应的整形,而不是字符串,所以如果对此有要求的时候,定义枚举的时候,按照字符串的顺序定义。
枚举的一个不好的地方是:修改枚举的时候,使用的是alter table操作。所以我觉得对于枚举,还是直接使用int类型,在服务层做转换比较好。

1.1.5 日期类型

不管什么日期类型,都只能精确到秒,如果需要存储微妙,则可以使用bigint存储微妙级别时间戳,或者double存储秒之后的部分。
timestamp时间范围是1970到2038,而且依赖时区,服务器、客户端连接都有时区设置。

1.1.6 位数据类型

bit,存储的是二进制,检索的时候查出来的是这个二进制表示的ascii值。
避免使用这种类型。

如果要存储boolean类型,true or false,可以使用可为null的char(0),char(0)占用一个位,可以表示两种情况,null或空字符串。(我觉得没必要)

1.2 schema设计陷阱

1.太多的列

mysql服务层通过存储引擎api,以行缓冲格式拷贝数据,然后在服务层解码成各个列,所以如果一行有太多的列的话,行缓冲数据解码成各个列的消耗是很大的。

2. 太多的关联

mysql限制了每个关联最多只能是61张表。太多关联的弊端不必说。

1.3.反范式

以更新代码换取查询性能,以及排序性能。
子表的统计数据记录在父表,省的每次查询都要count()统计子表。

1.4. 缓存表和汇总表

汇总表,把一些定时汇总,得到一个不精确的数据,加快查询。精确查询则,统计汇总表数据的和,加上汇总表数据到真实时间这个区间的数据和。

缓存表:把一个表的某些字段,缓存到另一个表中。

1.5 加快alter table的速度

线上alter table会锁住整个表,可以使用新结构创建一个空表,然后数据迁移完之后,重命名表替换旧表。有工具可以帮助做这一系列操作。比如:online-schema-change

你可能感兴趣的:(高性能mysql-数据类型与表结构优化(一))