MySQL的SQL语句优化

发布时间 2023-09-27 13:40:42作者: 初和

一、拿到SQL之后,用执行计划查看参数。

explain
select 1 from `d_ec_hyx`.`t_advertiser_info`
where 1 = 1
and f_corp_id = 15930142
and f_type in (1, 4)
and f_refund_status = 1 limit 1

 

二、 执行计划 ID。

  1、id 相同,执行顺序从上往下;

  2、id 不同,如果是子查询,id 的序号会递增,id 值越大优先级越高,越先被执行;

  3、id 有相同的又有不同的 (混合式的),即两种情况都存在,id 如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id 值越大,优先级越高,越先执行;

  4、id 为 null 的最后执行;比如 union 查询可能 id 为 null;

三、执行计划 select_type。查询的类型,主要是用于区分普通查询、联合查询、子查询等;

  SIMPLE:简单的 select 查询,查询中不包含子查询或者 union;

  PRIMARY:查询中包含子部分,最外层查询则被标记为 primary;

  SUBQUERY:SUBQUERY 表示在 select 或 where 列表中包含了子查询;

   MATERIALIZED:表示 where 后面 in 条件的子查询;

  UNION:若第二个 select 出现在 union 之后,则被标记为 union;

  UNION RESULT:从 union 表获取结果的 select;

四、执行计划 table。

  查询涉及到的表,直接显示表名或者表的别名;

  <unionM,N> ID 为 M 和 N 查询结果进行 union 后产生的结果;

  <subqueryN> ID 为 N 进行子查询产生的结果; 

五、执行计划 partitions。

  匹配的分区信息,如果查询基于分区表,将会显示访问的是哪个区;
  表分区:表分区是将一大表,根据条件分割成若干个小表,mysql 5.1 开始支持数据表分区,比如:某用户表的记录超过了 2 万条,那么就可以根据 id 将表分区,也可以根据时间分区,当然也可根据其他的条件分;

  比如:
  partition BY RANGE (uid) (

    partition p0 VALUES LESS THAN (10000),

    partition p1 VALUES LESS THAN (20000)

  );

  按照这种分区方案,uid 小于 10000 的所有行被保存在分区 P0 中,uid 在 10000 到 20000 的保存在 P1 中,依次类推;

六、执行计划 type【需要关注】

  访问类型,sql 查询优化中一个很重要的指标,结果值从好到坏依次是:
  system > const > eq_ref > ref > range > index > ALL
  system:const 类型的特例,基本不会出现,可以忽略不计;
  const: 表示通过索引一次就找到了,const 用于比较 primary key 或者 unique 索引,可以说是性能最好的,1;
  eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键 或 唯一索引扫描,性能次之,2;
  ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质是也是一种索引访问,性能次之,3;
  range:只检索给定范围的行,使用一个索引来选择行,性能次之,4;优化最坏的情况要达到这个级别 range;
  index:Full Index Scan,索引全表扫描,把索引从头到尾扫描一遍;
  ALL:Full Table Scan,扫描全表以找到匹配的行;

七、执行计划 possible_keys。查询过程中有可能用到的索引;

八、执行计划 key【需要关注】

  实际使用的索引,如果为 NULL,则表示没有使用索引;

九、执行计划 key_len。

  实际使用到的索引长度

十、执行计划 ref。

  当使用索引列等值查询时,与索引列进行等值匹配的对象信息;

十一、执行计划 rows 扫描行数【需要关注】

  根据表统计信息或索引选用情况,大致估算出找到所需记录所需要读取的行数;

十二、执行计划 filtered。

  表示返回结果的行数占需读取行数的百分比,filtered 的值越大越好,100 是最好的情况;

十三、执行计划 Extra。【需要关注】

  十分重要的额外信息,这些额外信息有:

  1、Using filesort:
  mysql 对数据使用一个外部的文件内容进行了排序,而不是按照表内的索引进行排序读取;

  2、Using temporary:
  使用临时表保存中间结果,也就是说 mysql 在对查询结果排序时使用了临时表,比如在 order by 或 group by;
  中间 MySql 处理过程需要多处理一个临时表,一般这种情况是需要优化处理的。
  优化处理一般使用索引优化;

  3、Using index:
  表示相应的 select 操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高 (不去回表操作);

  4、Using where:
  使用了 where 过滤条件;

  5、select tables optimized away:
  基于索引优化 MIN/MAX 操作或者 MyISAM 存储引擎优化 COUNT (*) 操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即可完成优化;

  6. 优化建议
    1. 所有表必须使用 Innodb 存储引擎
    没有特殊要求(即 Innodb 无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用 Innodb 存储引擎(MySQL5.5 之前默认使用 Myisam,5.6 以后默认的为 Innodb)。
    Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。

    2. 数据库和表的字符集统一使用 UTF8
    兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效,如果数据库中有存储 emoji 表情的需要,字符集需要采用 utf8mb4 字符集。

    3. 所有表和字段都需要添加注释
    使用 comment 从句添加表和列的备注,从一开始就进行数据字典的维护

    4. 尽量控制单表数据量的大小,建议控制在 500 万以内。
    500 万并不是 MySQL 数据库的限制,过大会造成修改表结构,备份,恢复都会有很大的问题。
    可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小

    5. 谨慎使用 MySQL 分区表
    分区表在物理上表现为多个文件,在逻辑上表现为一个表;
    谨慎选择分区键,跨分区查询效率可能更低;
    建议采用物理分表的方式管理大数据。

    6. 尽量做到冷热数据分离,减小表的宽度
    MySQL 限制每个表最多存储 4096 列,并且每一行数据的大小不能超过 65535 字节。
    减少磁盘 IO, 保证热数据的内存缓存命中率(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的 IO);
    更有效的利用缓存,避免读入无用的冷数据;
    经常一起使用的列放到一个表中(避免更多的关联操作)。

    7. 禁止在表中建立预留字段
    预留字段的命名很难做到见名识义。
    预留字段无法确认存储的数据类型,所以无法选择合适的类型。
    对预留字段类型的修改,会对表进行锁定。

    8. 禁止在数据库中存储图片,文件等大的二进制数据
    通常文件很大,会短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机 IO 操作,文件很大时,IO 操作很耗时。
    通常存储于文件服务器,数据库只存储文件地址信息

    9. 禁止在线上做数据库压力测试

    10. 禁止从开发环境,测试环境直接连接生产环境数据库