Mysql调优

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

由于MySQL本身的限制,并非所有的SQL都能的到很好的优化,实际业务场景中可能要辅以其他技术方案甚至业务妥协来实现当前业务的优化。

但就其开发成本而言,SQL优化是首选考虑的优化方向

文档说明

数据库版本: MySQL 5.7.16
数据库schema: MySQL Sakila
操作系统: CentOS 7.3


提示:以下是本篇文章正文内容,下面案例可供参考

一、SQL查询为什么会慢?

1. 较为宏观的视角可能有一下原因:

  • SQL 压根没建立索引
  • SQL 索引性能不好
  • SQL 尽管建立了索引,但是没走索引
  • SQL 使用了磁盘临时表
  • 其他业务锁表导致当前SQL查询等待
  • MySQL 选错索引

2. 从服务器资源的角度可能有以下原因:

  • 伴随SQL执行,CPU资源消耗100%
  • 伴随SQL执行,内存资源不够用,需要走磁盘I/O
  • 原服务器本身就繁忙,分配不到资源
  • 硬件资源本身就不足

二、SQL查询慢在哪里

Mysql排查工具

MySQL 官方提供以下两种工具让我们来排查SQL究竟慢在哪里

  • profile

profile

  • explain [extended] (MySQL 5.7 后extended已过时)

 explain

  • explain [extended]的几个重要参数
    • type: 判断SQL好坏的性能指标,从最优到最差依次: null>system>const>eq_ref>ref>range>index>All。一般来说,要保证查询达到range级别,最好达到ref;
    • key:判断SQL是否使用索引,使用了什么索引;
    • key_len:索引长度,主要受数据类型影响,长度越短,索引性能越好
    • rows:表明优化器大概要扫描的行数,值越小性能越好
    • extra:提供辅助优化信息,告诉我们应该从那个方向优化

三、SQL优化思路参考

优化JOIN连接查询

  • 常见的优化点有:
    • 关联字段建立索引(被驱动表建索引即可)
    • 已小表驱动大表(不用过多关注,MySQL一般情况会自动选择)
    • 合理设置join_buffer_size
  • JOIN SQL 执行通常为以下执行步骤
    • 将驱动表t1所有数据(N行)加载到join_buffer内存中(如果join_buffer_size大小足够装下驱动表数据)
    • 从t1中读入一行数据,取出关联字段到被驱动表t2(M行)遍历查找(如果存在索引,树级搜索)符合条件的数据作为结果集的数据
    • 重复第二步,直至完成t1的遍历
  • 从上面的执行过程可以看到
    • 增大join_buffer_size,可以将驱动表所有的数据装载进内存,不会使用磁盘临时表,可提升性能
    • 关联字段建立索引,可以让被驱动表以树级复杂度进行搜索,提升遍历性能
    • 整体执行时间复杂度接近 N * logM,以小表(N行)驱动大表可以提升性能

优化子查询

  • IN 型子查询优化(使用覆盖索引)
    在这里插入图片描述
  • JOIN 型子查询优化
    • 关联字段建立索引

    • 不需要的字段不要返回

    • 利用覆盖索引

      -

  • FROM型子查询优化
    • 对于这类查询语句,目前没发现有特别好的方法优化,唯一能做的就是通过减少子查询结果集的记录数或者返回列数或者设置group by相关配置参数(见下文),使得子查询走的是内存临时表,从而性能不至于太差
      在这里插入图片描述

优化ORDER BY排序

  • 优化order by主要是优化using filesort,使其可以利用索引排序。其优化手段之一为建立排序可用的联合索引
    • 要建立可用索引,我们需要知道那些情况下索引可以生效和不生效,考虑以下情况
      ORDER BY
  • 合理设置ORDER BY 相关配置参数
    • 增大max_length_for_sort_data(线程级)
    • 增大sort_buffer_size(线程级)
  • order by的另一优化方向是使用驱动表字段代替被驱动表字段,当然这是以语义相同为前提的
    -

优化ORDER BY分组

  • 前面所说的优化ORDER BY的方法同样适用于优化GROUP BY NULL来消除文件排序(当没用通过order by 指定排序列,group by会隐式按照分组的字段进行排序)
    -
  • 当GROUP BY无法使用索引,MySQL会使用临时表或文件排序处理分组。对于这种查询语句,可以通过使用hint提示SQL_BIG_RESULT/SQL_SMALL_RESULT来优化语句的性能
    • SQL_BIG_RESULT会提示优化器结果集很大,直接使用磁盘临时表排序,没必要先走内存表发现空间不够再走磁盘
    • SQL_SMALL_RESULT提示优化及结果集很小,可以直接使用内存临时表排序
      在这里插入图片描述

优化Limit分页

  • Limit分页的性能问题一般分为返回数据过大和偏移量大两种场景:
    • 对于返回数据过大的场景,缩小返回的行数即可
    • 对于偏移量大的场景,因为limit的实现机制是limit a,b会先扫描a+b数据,再截取[a,b)数据返回,所以最好能从业务上能控制分页的深度,如果业务不允许,则考虑是否可以使用延迟加载辅助定位方法
  • 延迟记载是指构造一个可使用覆盖索引的子查询,通过覆盖索引先查出索引列数据,再与自身关联返回需要的数据
    • 分页的深度越深,延迟关联相对于正常分页的性能就更明显。但是这种优化仅适用于单表查询
      在这里插入图片描述
  • 辅助定位是指将limit M,N查询转换为已知位置的查询(where id >= x limit N + 1),每次分页查询时,从上一页分页中取出id最大值x,构建 id > x条件
    -

优化UNION合并

  • MySQL总是通过创建并填充临时表的方式来执行union子查询,导致很多优化策略无法使用,经常需要手工将where,limit,order by等子句下推到union的各个子查询,以便优化器可用充分利用这些条件进行优化。
    • 尽可能使用union all 代替union,如果确实需要过滤重复行,请考虑代码实现过滤

优化Where子句

  • where子句的优化关键在于如何建立合适索引对数据进行过滤
    • 关联查询的where子句只能使用驱动表索引,如where t1.a = " and t2.b = "只能使用驱动表a的索引
    • 根据区分度建立联合索引
      • 对于区分度都不高的多列,考虑高频使用的列放前面
      • 对于区分度高的多列,考虑区分度高的列放前面
    • 结合order by列/group by列建立联合索引,如where t1.a= “” order by b考虑建立索引idx_ a _b;
    • 范围查询列放后面(范围查询列后面的列无法再使用索引),如where t1.a= " and t1.b in(’") and t1.c= "考虑建立索引idx_ a_ b或idx_ C. _b
    • 考虑根据枚举值拆分查询语句, 并发查询提高性能;

四、一些偏方(慎用)

  • 当关联查询驱动表选择不是小表导致查询慢时,可以考虑使用STRAIGHT_JOIN强制关联顺序
  • 明明有索引的时候执行要快很多,但是MySQL执行时并没有选择索引,可以使用Force Index强制SQL走索引
  • 如果整体业务并发不高,查询数据库也不算太频繁的情况,考虑开启查询缓存可以极大程度提交查询性能

总结

  • 通过profile/explain 工具基本可满足SQL性能分析的需求(高阶可以使用PT工具)
  • 根据相应的SQL特点(JOIN,ORDER BY…)可以针对性的进行SQL性能优化
  • SQL性能优化不是银弹,很多业务性能无法得到很好的优化,这只是开发成本最低的优化方向
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值