Mysql业务优化与设计
一、基于性能的表设计
- 根据查询设计好索引
- 根据核心查询需求,适当调整表结构
- 基于一些特殊业务要求调整实现方式
二、反范式,冗余必要字段
- 针对核心sql保留查询结果所必须的冗余字段,避免频繁JOIN操作;(根据范式要求设计的表字段,在实际查询时可能要查找到两个表才能找到,我们所要的结果,这样查两个表的代价比较高,所以有时可以做必要的更改,把核心SQL要用的结果放到一个表中。还有就是自动递增字段做为主键也是范式冗余的,不过这样做可以减少insert时间;表中增加更新时间字段使用timestamp类型,可以有助于日后的维护操作。)
三、拆分大字段
- 拆分大字段到单独的表中,避免范围扫描代价增大。如:将博文表拆分成两份。一份包含博文ID、主题、时间等信息,另一份包含博文ID和正文。因为人们在看博客时会先看标题信息,只对感兴趣的文章才会点进去看,都在一个表里的话,每次查询都要读出所有内容,I/O代价太高。
四、避免过多字段或过长行
- 行过长导致表数据页记录变少,范围扫描性能降低。
- 一次也没有必要获取那么多列的数据
- 更新数据页代价增加
- InnoDB是聚簇表,数字都存放在叶节点上,每个16K页最少放两行数据,一般一条记录超过8k的部分就会发生行迁移。
五、分页查询
(1)避免limit+offset过大
- 因为offset过大的话,数据库还是会实际扫描过offset条记录,因为数据库的优化关键点就是降低数据扫描量,所以这样的做法不可取。
- 取而代之的是使用自增主键ID模拟分页:(1)第一页直接查(2)获取第一页最后一行的ID值max(id)(3)第二页用where id > “max(id)” limit 100。这样每页的扫描代价是一样的,不会发生越向后扫描记录越多的IO代价。
(2)要求业务上禁止查询xx页之后的数据。
六、统计的实现方式
(1)触发器实时统计
最精准的统计方式,但会影响插入性能。
(2)最大自增ID实现统计
最容易实现并性能最佳,但中间不能有删除的记录。
(3)伪实时统计
在表中加入更新时间字段,每隔一段时间统计一次增量是多少,每次都根据上次更新的时间点,把最近更新的记录数加到一个统计表中。这样就实现了一个伪实时统计策略,而不会因为全表扫描影响数据库性能。
(4)利用缓存,在内存中记录统计数量
七、可扩展设计
(1)分库、分表拆分改进
MYSQL支持三种分区方式
- range分区(范围分区)从老分区读取数据时可以沿着分区扫描,不用回表;删除老数据(从分区中删除)也非常高效。
- list分区(垂直分区),适用于将来可能要基于地区、类目等方式垂直拆分数据的方式。
- hash分区,适用于将来需要做水平拆分的表。
由于MYSQ分区表的局限性,只有range分区经常使用:
- 索引只能建立本地索引,这对主键或唯一索引也是一样的,不像oracle有一个全局索引能保证整个表的某一字段的唯一性。主键或唯一键必须包含在分区字段内。
- 分区字段必须是整数类型或者一个字段套上某个函数可以返回一个整数类型。
(2)分布式改进
八、满足周边需求
(1)为周边需求额外增加表设计
- 为后台统计任务增加特殊索引
- 为数据迁移或统计需求增加时间戳字段。
(2)统计和后台需求
统计相关的维度和线上系统不一致。线上系统可能是通过用户ID来访问,而统计可能是通过地区或性别,所以线上系统的索引不适用于后台统计。
一般的处理方法:
- 利用MYSQL一主多从特性。主从数据库可以建立不同索引的特性将统计分流到特定从库。
- 包括一些特殊用户的批量查询,所有对线上有IO压力的查询都要读写分离。
- 加入timestamp类型字段做为更新时间,用于增量更新。