一、前言
今天下午吃下午茶,闲的无聊撩了下群友,问他如何MySQL性能优化知道哪些?
群友秒回:存储换性能、大字段拆分、查询优化、分区分表、读写分离。
一看这回复就是资深牛马了,对于常规的优化方式那是手拿把掐,当然仅仅只知道这些对于要成为架构师的南银还是不够的。下面我们直接上干货,简单来说MySQL性能调优分为几个方面,建表层面、sql层面、业务优化、读写分离&分库分表、硬件调整等等
二、MySQL性能调优
我们根据事务的常规发展顺序,从建表开始到硬件调整,逐一给大家讲解
2.1 建表层面
1.尽量选择较短的数据类型,尽量选择定长的数据类型
如:char 代替 varchar,固定长度处理速度更快
2.使用 not null 和 enum
如:省份、性别,可以使用 Tinyint 和 enum类型
3.使用索引
请注意甄别:哪些字段适合使用索引、是否使用联合索引
4.冗余字段
减少跨表查询,订单表里存储商品名称,而不用每次用商品id查询
5.派生列
减少每次查询的时候计算,比如用户总订单数,每次生成/取消订单的时候去计算,避免每次查询都消耗性能
2.2 sql层面
大多数CV工程师对这块很熟悉了,索引是否命中分很多场景
2.2.1 操作索引的操作符有
=, >, <, >=, <-, between, in, _, like
2.2.2 sql失效场景
- 查询条件使用函数
- 联合索引不符合最左匹配原则
- 查询条件使用not in 或 <>
- like通配符在左
- 查询语句用or
- 查询条件使用 is null 或 is not null
- 查询条件使用between 或 in,且查询范围较大,查询优化器觉得全表扫描更高效
2.3 执行效率分析
比较常用的 explain 语句, procedure analyse() 语句
2.3.1 explain 语句
语法: expalin sql,如:explain select * from students;

分析结果的含义:
- id:查询的序列号,表示查询的顺序,对于简单的查询,id通常为1,对于复杂的查询(如包含子查询或联合查询),id可能有多个值(联合查询explain后会返回多列数据)
- select_type:表示查询的类型
- simple:简单查询,不包含子查询或联合查询
- primary:最外层的查询
- subquery:子查询中的第一个select
- derived:派生表(from子句中的子查询)
- union:联合查询中的第二个或后续的select
- union result:联合查询的结果
- table:当前步骤涉及的表名;
- partitions:涉及的分区(如果表被分区)
- type:连接的类型,效率逐渐变高
- ALL:全表扫描
- index:全索引扫描
- ranage:范围扫描
- ref:非唯一索引扫描
- eq_ref:唯一索引扫描
- const:常量值
- system:系统表(只有一行数据)
- possible_key:查询可以利用的索引名;
- key:实际使用的索引;
- key_len:索引中被使用部分的长度(字节);
- ref:显示列名字或者"const"(不明白什么意思);
- rows:现实MySQL认为在找到正确结果之前必须扫描的行数;
- extra:MySQL的建议;
2.3.2 analyse() 语句
语法如下:
sql
sql procedure analyse([max_elements [,max_memory])

- max_elements:指定每列非重复值的最大值,默认值为 256。当超过这个值时,MySQL 不会推荐 ENUM类型。
- max_memory:为每列找出所有非重复值时可能分配的最大内存数量,默认值为 8192 字节。
- Field_name:字段名称。
- Min_value:该字段的最小值。
- Max_value:该字段的最大值。
- Min_length:该字段值的最小长度。
- Max_length:该字段值的最大长度。
- Empties_or_zeros:该字段为空或零的值的数量。
- Nulls :该字段为
NULL
的值的数量。 - Avg_value_or_avg_length:该字段的平均值或平均长度。
- Std:该字段的标准差。
注意:此工具在MySQL8.0下架,原因是认为没人使用
移除声明:dev.mysql.com/worklog/tas...
2.4 业务优化
需要根据具体来处理
2.5 读写分离&分库分表
一般来讲,读写分离需要业务做的较大才有用,否则就是大炮打蚊子,下面我给出一个电商的案例
一个电商项目,有商品、库存、订单、购物车、支付,甚至是用户画像等内容,用户从零到千万级别,日增数据亿级,那么用单一的数据库就无法保证正常使用了,MySQL的CPU和IO占用可达到90%以上,slow sql概率大大提高,此时需要做读写分离,由于数据库功能分解,商城的主要压力来自于读,从库可水平扩展,所以暂时缓解了MySQL的压力。但是随着数据量变大,MySQL主从同步变得十分频繁,甚至出现我下单之后查不到的情况,这是read-after-write问题,master还没同步数据到slave数据库,可以根据show slave status查看,Seconds_Behind_Master值,代表主从同步从库落后主库的时间,单位为秒,若主从无延迟,则为0,如何处理主从延迟?比如增大innodb_buffer_pool_size,使用物理机,使用SSD磁盘,升级高版本MySQL,支持并行主从复制。
读写分离解决了读压力,每次读压力增加,可以通过加从库的方式水平扩展,但是写操作的压力会随着业务爆发式增长没有得到有效缓解,发现一次普通的insert操作,甚至长达一秒以上。此时,Master数据库成为性能瓶颈,那么我们再次将主库根据业务做垂直拆分,每个系统进访问对应业务的数据库,尽量避免或减少垮裤访问。垂直分库过程,也有不少挑战,最大的挑战是:不能跨库join,同时需要对现有代码重构。
读写分离,解决了读压力。垂直分库通过按业务拆分主库,缓存了写压力,但系统依然存在隐患:如单表数据量越来越大, 订单表过亿,超出MySQL极限,影响读写性能。此时需要对MySQL进一步水平拆分,问题是,根据什么逻辑拆分?第一种是根据城市拆分,另一种是根据ID拆分,按城市拆分聚合查询简单,但是数据分布不均匀,有的热点城市数据量极大,冷门城市数据很少。根据id拆分则出现跨库数据难聚合的情况,最终采取根据id拆分,从架构上,将系统分为三层:应用层,数据访问层:统一的数据访问接口,屏蔽读写分库、分表、缓存等技术细节,数据层:对DB数据进行分片,并可动态的shard分片
2.6 硬件调整
1.在机器上装更多的内存;
2.增加更快的硬盘以减少I/O等待时间;
寻道时间是决定性能的主要因素,逐字地移动磁头是最慢的,一旦磁头定位,从磁道读则很快;
3.在不同的物理硬盘设备上重新分配磁盘活动;
三、总结
想弄懂MySQL性能调优能从哪些方面下手,首先得理清sql执行链路,以及涉及的模块,平时在工作中经验经验,在学习中好好总结。
再次提醒:MySQL5.7和MySQL8.0有非常大的改动,注意项目MySQL服务使用版本
详情请查看以下链接: