暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL优化: 大表优化

系统工程实验室 2021-05-17
2636

数据库性能瓶颈点

  • 高QPS和TPS导致SQL处理效率下降

  • 高并发导致的数据库连接数被占满和超高的CPU占用率导致服务器资源耗尽而不可用。

  • 磁盘IO性能瓶颈导致数据传输效率下降,计划任务导致磁盘IO下降。

  • 网卡IO性能瓶颈

大表带来的问题大表一般情况下可以参考两个经验值
  • 单表行数超过1000W

  • 单表大小超过10G

以上并不是绝对概念,还是要实际分析当前数据量情况下的性能,在必要的时候再进行大表的优化处理。一般情况下,如果单表的数据量上线达到3000W是必须要考虑优化了,比如进行分表处理。

当MySQL出现大表时往往会引起如下问题

  • 查询性能降低,出现慢SQL:特别是区分度低的一些查询,磁盘IO增多,搜索响应变慢。

  • DDL灾难:大表建立索引锁表,修改表结构导致长时间锁表,表结构修改导致主库操作阻塞,数据库连接被打满导致服务不可用,而且如果有主从集群则情况更加严重。

慢SQL的出现会导致MySQL集群响应变慢,CPU/内存资源耗用增大,连接数饱满,阻塞客户端访问,对系统的可用性造成严重影响,这是需要在实际项目中尽量避免。

大表优化常见策略

优化表字段

基本原则是:保持字段的简单、确定性并合理建立索引
  • 选择简单的数据类型:能满足业务需求前提下,选择更加简单的数据类型。

  • 使用合理的字段长度:例如如果能明确确定字符串长度,则使用char而非varchar

  • 表字段尽量避免null值出现,建议设置为not null,并使用默认值

  • 使用枚举或整数代替字符串类型:需要注意的是使用整数对后续

  • 尽量使用TIMESTAMP而非DATETIME

  • 单表不要有太多字段,过多字段建议进行垂直拆分

  • 建议不使用外键约束,由业务层保证约束

  • 尽量少用text/blob等大字段,如果必须则可以考虑拆分

  • 建议考虑适当的冗余设计来避免频繁的连接操作

优化索引

  • 在合适的字段上建立索引,例如在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描

  • 值区分度很小字段不适合建索引,即使建立了索引,查询优化器可能会放弃使用索引。例如"性别"这种只有两三个值的字段

  • 字符字段最好不要做主键:字符字段的顺序性

  • 联合索引注意顺序和查询条件保持一致,同时删除不必要的单列索引,离散度大的列放在联合索引前面。

优化SQL语句

  • 避免在WHERE子句中对字段进行NULL值判断,否则将导致索引失效从而进行全表扫描

  • 避免select *,尽量细化要查询的字段,减少网络带宽占用

  • 合理利用覆盖索引,尽量避免回表操作。回表往往意味着随机IO,是性能优化的重点。

  • 建议使用连接查询来代替子查询

  • 大批量更新操作建议分拆为小批量:顺序或并发小批量更新

  • 尽量不在列上进行运算,例如使用数据库教程函数、计算表达式:任列的操作都将导致索引失效引起全表扫描

  • SQL语句尽可能简单:一条SQL只能在一个cpu运算,大语句拆小语句,减少锁时间

  • OR可以改为IN或者union查询:OR会导致后续索引失效,小数据量范围的in查询会使用索引(IN的个数建议控制在200以内,但不绝对,IN查询项数量过多时,查询优化器会放弃索引进行扫表)

  • 使用同类型进行比较,比如用'123'和'123'比:数据类型不一致时,MySQL会自动进行函数运算导致索引失效

  • 避免在WHERE子句中使用!=或<>操作符,否则会导致索引失效而进行全表扫描

  • 连续数值使用BETWEEN不用IN

  • 列表数据建议使用分页,限制每页数据大小;对偏移比较大的分页建议改为连接查询

分表

一般不建议使用分区,分表可以分为水平拆分和垂直拆分。

垂直拆分垂直拆分从列的维度将单表进行拆分,将部分列从主表移到其他表,降低单表的数据量大小。垂直拆分的场景:

  • 表的字段过多:在MySQL单表字段过多可以拆分

  • 表中存在大字段:单表中的TEXT或BLOB等字段

  • 字段冷热分离:将基础常用字段和冷字段进行分开存储

垂直拆分带来的问题是:

  • 原理单表查询操作,需要从多个垂直表中查询才能汇总成一条完整记录,增加网络IO次数。当然也可以进行连接查询,具体情况具体分析。


水平拆分

水平拆分将单表按照行拆分成多张表,以降低单表数据量。水平拆分的场景

表数据行数过多
水平拆分需要在业务层有合理的数据路由策略,可以根据例如记录ID/用户ID等规则进行路由。水平拆分导致的问题是增加了查询的复杂度:原理在单表中一条SQL完成的查询,可能会分散的不同表中进行,同时需要对查询结果进行汇总处理。当然,开发人员可以在业务层自己处理,一般会借助成熟的中间件进行辅助处理。

分库
严格上分库解决的不是大表问题,典型情况下,系统进行微服务拆分之后,内聚的领域会有独立的数据库。另外,分库也是一种水平伸缩策略,有助于均衡高并发流量。

MySQL迁移到NoSQL相比如MySQL,NoSQL类型数据库在大数量上具有更多优势,当MySQL数据量过亿,或是持续快速增长时,切换数据库也是可选方案之一。例如,AWS的DynamoDB(收费)具有优秀的性能,同时支持事务,理论上单表容量可以无限扩容,支持PB级的存储。同时,百亿级别的单表数据,DynamoDB也支持毫秒的查询响应延时,基于DAX加速甚至可以达到微秒级别。

在之前的项目中我们后端数据库选择的是DynamoDB,用户量在十亿以上,设计上不需要进行分表处理,单表容纳所有数据,系统自动扩容,性能表现优异。

另外,也可以考虑实践像TiDB这种数据库,目前社区也比较活跃。迁移数据库带来的问题研发成本增加总结大表导致的慢查询是致命的,慢SQL会导致系统吞吐量急剧下降并导致大量请求不可用。因此,需要格外关注慢SQL,并建立及时的监控报警机制,当发现慢SQL时及时介入处理。当单表数据量超过千万时工程师就应该关注可能存在的性能问题了,并基于具体情况进行可行的优化。

文章转载自系统工程实验室,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论