【MySQL】大表优化

 

当MySQL单表记录数太多时,各项操作性能会明显下降。

相关优化策略有很多,但肯定不是所有策略都适合某个特定案例。也许在技术层面上,很多策略都能提高性能,但我们也得考虑成本和收益(ROI)。

另,是否有性能提升,或者说从整体业务角度而言综合性能表现是否更符合需求,需要经过实战测试才能确定。

 

此文仅提供几种常见优化方案。对于某些遗留项目【其它方案】中提及的途径可能更合适 —— 优化SQL语句和索引,甚至设计表结构。

 

1. 限制数据范围

通过限定数据操作的范围可以有效减少数据量。

现实业务中需要一次获取大量数据记录的场景其实是比较少的。如,对于查找账单的需求,可以按月份查找,而非一次性获取所有账单。

 

2. 缓存数据

缓存一般适合数据改动频率较低的场景。如果改动频繁,就没有缓存的意义,还会为了维护缓存而增加过多开销。

在具体业务场景链路中哪些位置设置缓存也需要认真考量。持久层框架(如,MyBatis)、应用服务层、Web页面、浏览器客户端都可以做缓存。

 

注意:MySQL的查询缓存从5.6开始默认是关闭的,在5.7.20中被“deprecated”,在8.0中被移除。因为MySQL的查询缓存实现中用到了全局互斥锁,所以会降低性能(无法有效发挥多核服务器的优势)。

 

未认真考量缓存对业务的影响,特别是缓存失效时如何更新数据,是大多数新手会犯的典型错误。这种情况在新手写的应用服务层代码中出现频率较高。

 

建议:永远不要单人作战引入缓存机制。必须向产品经理等领导详细推演展示缓存机制的影响,确保相关干系人心中有素。认真听取老手的建议,切不可犯蠢。

 

在实际项目中,拍板做决定的人理解相关细节的影响是非常重要的。我见过一个案例中,产品组内掌握话语权的人集体“犯蠢/宕机”,未听取专家建议,在没有任何缓存失效应对方案的情况下使用缓存,结果在用户环境暴露重大缺陷,最后彻底弃用缓存。“将熊熊一窝”、“统帅无能,累死三军”说的就是这类情况。

 

3. 读写分离

写主库,读从库。

 

这种方案适用于 读操作 远多于 写操作的情况,而且需容忍读操作所得数据不是最新数据的情况。

 

How to Setup MySQL (master-slave) Replication in CentOS 7

How to Set Up Master Slave Replication in MySQL

Replication and Binary Logging Options and Variables

 

4. 表分区

表分区是一种特殊的 水平拆分 方式,也是一种特殊的 库内分表。

它可以 缓解 单一表数据量过大 的问题,

但它对于减轻数据库服务器压力的作用不大,所有请求还是在争用同一服务器的资源。

 

MySQL 的 InnoDB 分区表就是典型代表。其优缺点:

优点:

  • 方便对数据分而治之

    • 可以通过删除分区来删除无用的数据。

    • 为新增数据新开分区可以加快执行效率。

    • 可以对分区单独优化、检查、修复、备份、恢复。

  • 提高数据查询效率

    可通过查询条件排除不符合条件的分区,提高效率。

 

缺点:

  • InnoDB 分区表不支持外键。

    分区表不能引用其它表中的列作为外键;

    其它表不能引用分区表中的列作为外键。

  • 不支持全文索引(Fulltext Index)。

  • 不支持空间类型数据(如,Point、Geometry)。

  • 分区索引不支持子查询。

 

5. 垂直拆分

注:垂直拆分后单表数据量未变,依然很大(需要水平拆分)。

 

垂直分库

优点:可以用 不同数据库 支撑不同的业务,降低单个数据库的压力。

缺点:

  • 上层业务需额外处理不同的数据源;

  • 可能存在数据一致性维护问题。

 

垂直分表

优点:

  • 表结构简化,易于维护;

  • 单行数据量变小,一个数据块可存放更多行数据,可减少IO次数。

缺点:

  • 需管理额外的主键冗余列;

  • 事务处理复杂;

  • 需要额外的表连接操作(join)。可在业务服务器join,减少数据库压力。

 

6. 水平拆分

优点:不存在单表数据量过大 和 请求高并发 的性能问题,提高了系统的负载能力 和 稳定性。

 

缺点:

  • 数据分片的事务一致性难以解决。

  • 跨节点 Join 的逻辑复杂,性能差。

  • 数据多次扩展 的 难度 和 维护量 非常大。

 

7. 其它方案

出于各种原因,与上面那些方案相比,这些方案的普适性可能会更小一点。像“优化SQL语句” 和 “优化索引” 可能在遗留项目中更适用。

甚至 重新设计表结构 是最佳方案,既优化数据库读写性能,又可以重构上层过时的业务逻辑。

 

7.1 使用更紧凑的数据类型

更紧凑的数据类型可以减少对存储空间的需求,读写开销也会更小。如:

  • 不要让单表包含太多字段。

  • 用 TINYINT 等占用空间更小的类型替代 INT。对于无负数的情况,还可将其设置为 Unsigned。

  • 【谨慎】用 ENUM 或 整型 替代字符串类型。

    因为 ENUM降低了可扩展性,整型降低了可读性。如,用整型存储IP就是牺牲了可读性。

  • 【谨慎】使用 TIMESTAMP 替代 DATETIME。

    因为 TIMESTAMP 虽然可减少所需存储空间,但放弃了时区信息(被统一为UTC),且存取数据需要额外的时区转换操作。

我遇到过一个项目,用UUID作为主键。

UUID的正常显示格式是带有连接号(-)的。如,“1ccaf0bc-46fd-11e9-b210-d663bd873d93”。

业务不需要显示连接号,所以生成UUID时会去除连接号。

存储时将通过MySQL的 unhex() 方法 将其转换为二进制;读取时通过 hex() 方法转换为16进制数字串;字段类型为 binary(16),比 char(32) 更节省空间。

 

7.2 优化索引

索引的优化也是非常需要大规模多场景实战性能测试的。很多时候会展现出令人沮丧的结果——还不如“优化”前快。

  • 索引(尽可能)恰到好处。

    可根据 Where 和 Order By 语句中涉及的字段建立索引。用 Explain 命令查看查询语句用了索引还是全表扫描。

    重复值过多的字段也不适合建索引。

    索引过多,消耗的存储空间越多,维护开销也多,MySQL在判断使用哪个索引时也更耗时(每次查询只能用一个索引)。

  • 多列索引的使用需注意查询条件是否能启用该索引。多列索引通常由于拆分的单列索引。如果所查字段都在多列索引内,可以减少读取操作。

  • 【谨慎】用自增字段作为主键。通常应选取具有实际业务意义的字段作为主键。

  • 【谨慎】不使用 外键、UNIQUE 等约束,由上层业务程序提供保障。

 

7.3 优化SQL语句

优化SQL语句是个大话题,内容繁杂。SQL语句的小技巧很多。如,使用 in 而不是 or;对连续值使用 between 而不是 in。

此外还需要非常了解业务。新工程中SQL语句不合适的情况较少。而对于遗留的数据库,特别是原本业务就复杂的情况,很可能会因表结构设计不合理,导致SQL语句逐渐演变性能越来越差。

 

关于慢查询分析。

慢查询日志(slow_query_log)是最常被提及的方式。但是这种方式非常繁琐。前期准备工作麻烦。为了运行分析工具 mysqldumpslow 还要装 perl。各种变量的设置容易出错,打断工作流程。各项步骤对人很不友好。

通过MySQL Workbench 使用 Performance Schema 方便多了。

 

7.4 使用不同类型的数据库

某些场景确实无法发挥出 MySQL InnoDB 的优势。

对于读密集的场景 MyISAM 因可能更有性能优势。

有些数据也不适合用关系型数据库存储。可以考虑一些NoSQL数据库。

如,日志、监控数据、非结构化 或 弱结构化的数据、对事务和关联操作支持要求较低的数据 等。

 

7.5 升级硬件

这个方案往往会被认为没有技术含量。但很多时候却真的是很值得考虑的方案。

通过收集服务器性能指标,结合业务进行分析,得出恰当的硬件升级方案也不是件容易的事。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值