数据库优化

79 阅读9分钟

优化步骤

数据库优化步骤.png

查看系统性能参数

 show [global | session] status like '参数';
 一些常用的性能参数:
 connections:连接MySQL服务器次数
 uptime:MySQL服务器的上线时间
 slow_queries:慢查询次数
 innodb_rows_read:select查询返回的行数
 innodb_rows_inserted:执行insert操作插入的行数
 innodb_rows_updated:执行update操作更新的行数
 innodb_rows_deleted:执行delete操作删除的行数
 com_select:查询操作的次数
 com_insert:插入操作的次数,批量插入只累加一次
 com_update:跟新操作的次数
 com_delete:删除操作的次数
 ​
 show status like 'slow_queries';
 show status like 'innodb_rows%';

统计sql查询的成本

 show status like 'last_query_cost'; -- value:数据页

定位执行慢的sql

 -- 开启慢查询日志参数
 show variables like '%slow_query_log%';
 ​
 -- 开启慢查询日志
 set GLOBAL slow_query_log = 'ON';
 ​
 -- 查看慢查询时间阈值
 show variables like '%long_query_time%';
 ​
 -- 修改慢查询阈值(global and session 级别变量)
 set global long_query_time = 1;
 ​
 -- 查看慢查询记录
 show status like 'slow_queries';

分析工具

 慢查询日志分析工具:mysqldumpslow
 ​
 分析优化器执行计划:trace(mysql自带)
 ​
 MySQL监控分析视图:sys schema
 ​
 -- 查看当前线程执行情况,如果发现了异常的sql语句,可以直接kill掉,确保数据库不会出现严重的问题
 show processlist

查看sql执行开销(mysql cmd界面操作)

 -- 查看并开启会话级别的执行概述
 show variables like 'profiling';
 set profiling = 'ON';
 ​
 -- 执行相关查询后再查看近期的查询开销
 show profiles;
 ​
 /**
   查看指定query_ID的开销
   all:显示所有的开销信息
   block io:显示块io开销
   context switches:上下文切换开销
   cpu:显示cpu开销信息
   ipc:显示发送和接收开销信息
   memory:显示内存的开销信息
   page faults:显示页面错误开销信息
   source:显示source相关的开销信息
   swaps:显示交换次数的开销信息
 */
 show profile for query 1;
 show profile all ,block io for query 1;

Explain

数据库优化步骤-explain执行计划显示列.png

 -- explain字段解析
 (重点)id:在一个大的查询语句中,每个select关键字都对应一个唯一的id。
 1 一个select对应一个id,explain会出现多行相同id
 2 id相同为一组,从上往下执行;
 3 在所有组中,id值越大就越先执行;
 4 每个不同的id标识一趟独立的查询,一个sql查询次数越少越好
 ​
 select_type: 查询类型
 1 select关键字对应的查询类型,确定小查询在大查询中扮演的角色。
 2 查询中不包含 union 或 子查询,都算作simple类型
 ​
 table:输出行所引用的表的名称
 1 可观察产生的临时表 | extra:using temporary
 2 首行: 驱动表,往下: 被驱动表
 ​
 partitions:代表分区表中的命中情况,非分区表该值为 null,一般情况下该值都为 null。
 ​
 (重点)type: 执行查询时的访问方法/访问类型
 1 system:当表中只有一条记录,且该表使用的存储引擎的统计数是精确的,如MyISAM、Memory
 2 const:对单表的主键或unique索引列与常数进行等值匹配
 3 eq_ref:在连接查询时,如果被驱动表(join右侧的表)是通过主键或unique索引列,进行等值匹配的方式进行访问的(如果该主键或unique索引是联合索引的话,所有的索引列都必须进行等值比较)
 4 ref:当通过普通的二级索引与常量进行等值匹配
 ​
 possible_keys:可能用到的索引
 ​
 key:真实用到的索引
 ​
 key_len:实际用到的索引长度(单位:字节数)
 1 主要针对联合索引,值越大越好:4个字段构成联合索引共200bytes,若值为200说明可能只用到联合索引中的前两个
 ​
 ref: 当使用索引列进行等值查询时,与索引列进行等值匹配的对象信息
 ​
 rows: 预估需要读取的记录条数
 ​
 filtered:查询表经过搜索条件过滤后,查出数据条数/过滤剩余记录条数的百分比,值越大越好,说明使用的数据页少
 1 连接查询时关注: 它决定被驱动表要执行的次数(rows * filtered)
 ​
 (重点)Extra: 额外信息
 1 using filesort: 性能低下需规避
 ​
 -- explain其他输入格式
 explain format = json select SID from tb_relation_student;
 ​
 -- 通过诊断语句查看当前会话中执行语句所产生的条件(错误、警告和注释)的信息(cmd界面)
 show warnings ;

InnoDB SQL优化

关联查询优化

  • *** 关联查询小结果集驱动大结果集(本质就是减少外层循环的数据数量)(小的度量单位:过滤后的表行数*每行大小)
  • 查询优化器会自动优化sql,被驱动表不一定是left join 右侧的表,主要看explain 同id最下方表名
  • 优先给被驱动表的过滤条件添加索引(一般是 t2.name),类型要一致,否则类型转换索引失效
    • select * from t1 left join t1 on t1.name = t2.name

子查询查询优化

  • 不建议使用子查询,执行效率不高

    • 执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表,然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。这样会消耗过多的 CPU和 IO资源,产生大量的慢查询
    • 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表,都不会存在索引,所以查询性能会受到一定的影响
    • 对于返回结果集比较大的子查询,其对查询性能的影响也就越大
  • 优化

    • 能够直接多表关联的尽量直接关联,不应子查询(减少查询的次数)
    • 不建议使用子查询,建议将子查询 sql拆开结合程序多次查询,或使用 join来代替子查询
    • 衍生表建立不了索引
    • 尽量不要使用 not in 或者 not exists,用 left join...on...where...is null 代替

order by排序优化

  • 在 order by字段上加索引,避免 select *

    • 在 MySQL中支持两种排序方式,分别是 filesort 和 index排序

      index排序中,索引可以保证数据的有序性,不需要再进行排序,效率更高

      filesort排序一般在内存中进行排序,占用CPU较多。如果带排结果较大,会产生临时文件IO到磁盘进行排序的情况,效率较低

  • 优化

    • sql中可以在 where子句和 order by子句中使用索引,避免在 where子句的全表扫面,order by子句的filesort排序

    • 尽量使用 index完成 order by排序,如果 where和 order by后面是相同的列就使用单索引列,如果不同就使用联合索引

    • 无法使用 index时,需要对 filesort方式进行调优

      filesort算法:双路排序和单路排序

      调优:提高 sort_buffer_size, max_length_for_sort_data

    • order by 后使用 limit,数据量小可以使用索引后回表

      create index idx_age_classid_name on student(age,classid,name);

      select * from student order by age,classid limit 10;

    • 覆盖索引可以提升效率 create index idx_age_classid_name on student(age, classid,name);

      select age, classid, name from student order by age,classid;

  • order by 索引失效

    • 需要回表的数据量过大导致索引失效,查询优化器会直接使用filesort进行排序

      create index idx_age_classid_name on student(age,classid,name);

      select * from student order by age,classid;(数据量小时使用索引,大时失效,以explain为准)

    • order by顺序错误索引失效:跳过联合索引最左字段

    • order by规则不一致,索引失效

      联合索引默认按每个字段升序排序,多字段排序时,一个字段升序,一个字段降序,索引失效

group by 分组优化

  • group by即使没有过滤条件用到索引,也可以直接使用索引。
  • group by先排序再分组,遵照索引建立的最佳左前缀法则
  • 当无法使用索引列,增大 max_length_for_sort_data 和 sort_buffer_size 参数设置
  • where效率高于 having,尽量再where时写限定条件
  • 减少使用group by,尽量在程序端做。order by、group by、distinct较为耗费CPU
  • 包含order by、group by、distinct这些查询语句,where条件过滤出来的结果集保持在1000行以内,否则 sql会很慢
  • 和order by优化差不多

分页查询优化

 -- 通过创建覆盖索引提升深度分页的性能,避免 innodb进行索引的二次查询(回表)
 ​
 -- 在索引上完成排序分页的操作,最后根据主键关联回原表查询所需要的其他列内容
 select * from t1, (select id from t1 order by id limit 2000000,10) t2 where t1.id = t2.id;
 ​
 -- 该方案使用于主键自增的表,可以把 limit查询转换成某个位置的查询(不用回表)
 select * from t1 where id > 2000000 limit 10;

覆盖索引

  • 覆盖索引:一个索引包含了满足查询结果的数据就叫做覆盖索引

  • 优点

    • 避免innodb进行索引的二次查询(回表):innodb是以聚簇索引的顺序来存储的,覆盖索引避免了对主键的二次查询,减少了io操作
    • 可以把随机 IO变成顺序 IO加快查询效率:使用二级索引获取到需要的记录id后,回表时使用的id可能时无序的
  • 弊端:维护代价高,索引冗余

  • 覆盖索引下,(!=)( like %)不一定失效

    create index idx_age_classid_name on student(age, classid, name);

    select age, classid, name from student where age != 20;

    select age, classid, name from student where name like '%德';

索引条件下推

 -- ICP: Index Condition Pushdown
 ​
 -- 主要针对联合索引,在使用左侧通配符时也能使用索引
 create index idx_age_classid_name on student(age, classid, name);
 select * from student where age = 20 and classid like '%2%' and name like '%德%'
 ​
 -- 关闭索引下推,默认开启
 SET OPTIMIZER_SWITCH  = 'index_condition_pushdown=off'

其他查询优化策略

  • exists 和 in 的区分(索引时前提)

    • select * from t1 where xx in ( select xx from t2 ); t1表 大于 t2表用 in,此时 t2是外循环(小表驱动大表)
    • select * from t1 where exists ( select xx from t2 where t2.xx = t1.xx ); t1表 小于 t2表用 exists,此时 t1是外循环(小表驱动大表)
  • select(*)

    • MySQL在解析过程中会通过查数据字典将 * 解析为所有列明,会大大的消耗资源和时间。
    • 无法使用覆盖索引
  • limit 1 对优化的影响

    • 若确定结果集只有一条,加上 limit 1,在找到一条结果后就不在继续扫描了,加快查询速度
    • 若对字段建立了唯一索引则不需要加上 limit 1
  • 多使用commit:在程序中多使用commit会因为资源的释放而提高程序的性能,释放的资源如下

    • 回滚段上用于恢复数据的信息
    • 被程序语句获得的锁
    • redo / undo log buffer 中的空间
    • 管理上述3种资源中的内部花费