优化分析
一、存储引擎的对比
二、性能下降SQL慢 、执行时间长、等待时间长
- 查询语句复杂
- 索引失效(单值索引、复合索引)
- 关联查询有太多join(设计缺陷或不得已的需求)
三、常见的Join查询
- SQL执行顺序:手写、机读
- 手写顺序:
#sql语句的手写顺序 SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOIN <right_table> ON <join_condition> WHERE <where_condition> GROUP BY <group_by_list> HAVING <having_condition> ORDER BY <order_by_condition> LIMIT <limit_number>
-
机读顺序:
#机读顺序 FROM <left_table> ON <join_condition> <join_type> JOIN <right_table> WHERE <where_condition> GROUP BY <group_by_list> HAVING <having_condition> SELECT DISTINCT <select_list> ORDER BY <order_by_condition> LIMIT <limit_number>
总结:https://database.51cto.com/art/201911/605471.htm
四、七种Join如下所示:
注意:mysql不支持全外连接,但是可以使用union(联合+去重)把左外连接和右外连接联合起来。
五、索引
定义:索引(Index)是帮助MySQL高效获取数据的数据结构 。索引的目的在于提高查找效率,类比字典。除了数据本身之外,数据库还维护一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高效的查找算法,这种数据结构就是索引(排序好的快速查找的数据结构)。
一般来说索引本身很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。
索引的优势:
- 提高数据检索的效率,降低数据库的IO成本,类似大学图书馆建书目索引
- 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗
索引的劣势:
- 索引可以看成一张表,也要占用空间。
- 索引会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
索引的分类:
- 单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
- 唯一索引:索引列的值必须唯一,但允许有空值(比如身份证号就是唯一的,可以做唯一索引)
- 复合索引:即一个索引包含多个列
mysql索引结构:
- BTree索引
- Hash索引
- full-test全文索引
- R-Tree索引
哪些情况需要创建索引:
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引
- 查询中与其他表关联的字段,外键关系建立索引
- 频繁更新的字段不适合创建索引(因为每次更新不单单是更新了记录,还会更新索引)
- where条件里用不到的字段不创建索引
- 在高并发下倾向于创建组合索引
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序的速度
- 查询中统计或者分组的字段
性能分析:
1、Explain:使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。
2、使用方法:
EXPLAIN SELECT * FROM <table>;
3、explain的作用:
通过EXPALIN,我们可以分析出以下结果:
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
执行计划各个字段的含义
1. id: select查询的序列号,表示查询中执行select子句或操作表的顺序
一、id会出现三种结果:
1.id相同,执行顺序由上至下
有上图可见,操作表的id号均为1,所以操作顺序为
d->l->employees
2. id不同时,id值越大优先级越高,越先被执行
如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
3. id既有相同的,又有不同的
说明:id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
注意:执行过程中,衍生的表用derived表示。
二、查询类型select_type
作用:用来表示查询的类型,主要用于区别普通查询、联合查询、子查询等复杂查询
- SIMPLE: 简单的select查询,查询中不包含子查询和UNION
- PRIMARY: 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY
- SUBQUERY: 在SELECT或WHERE列表中包含了子查询
- DERIVED: 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中
- UNION: 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层的SELECT将被标记为DERIVED
- UNION RESULT:UNION的最终结果
eg.
explain select * from employees e left join departments d on e.department_id = d.department_id union select * from employees e right join departments d on e.department_id = d.department_id;
三、table
table就是操作表
四、type
type表示使用了那种类型的查询,分为以下几种:
- ALL
- index
- range
- ref
- eq_ref
- const
- system
- NULL
查询效率从高到低依次为:
system > const > eq_ref > ref > range > index > all
一般来说,需要保证查询至少达到range级别,最好能达到ref。
- system:表只有一行记录,很少出现
- const:表通过索引一次就找到了,const用于比较primary key或者unique索引。
select * from table where id(主键) = XX;
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。
- ref:非唯一性索引扫描,对于每个索引键,表中可以找到多个符合条件的行。
- range:只检索给定范围的行,比如在where语句中出现between、<、>、in等查询
select * from table where id between 30 and 60;
- index:index与all的区别在于index类型只遍历索引树,这通常比all快,因为索引文件通常比数据文件小(也就是说all和index都是读全表,但index根据索引扫描然后回表取数据,而all是从硬盘中读取)
select employee_id from employees;
- all:遍历全表
select * from employees;
五、ref
将列与索引进行比较,主要用来表示表的连接条件(即哪些列或常量被用于查找索引列上的值)
#案例:查找部门编号为1700的员工信息 explain select * from employees e, departments d where e.department_id = d.department_id and d.location_id = 1700;
分析结果可以看出:d表的location_id索引列匹配了一个常量(1700);e表的department_id索引列匹配了d表的department_id。他们之间的匹配关系我们可以清楚地从ref中查看到
六、rows
根据表的统计信息和索引的选用情况,大致估算出找到所需记录所需要读取的行数,rows列中的值,越小越好。
七、Extra
-
Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,这种无法利用表内索引进行的排序操作为“文件排序”。(不好)
explain select last_name, department_id from employees order by salary;
- Using temporary:说明使用了临时表保存中间结果,常见于order by 和group by。(不好)
-
Using index:select操作中使用了覆盖索引(Covering Index)。
覆盖索引,也叫索引覆盖:select的数据列只用从索引中就能够取得,不必从数据表中读取。mysql可以利用索引返回select列表的字段,即查询的列要被所建的索引覆盖
explain select employee_id from employees;
1,SIMPLE,employees,null,index,,dept_id_fk,5,null,107,100,Using index
- Using where:使用了where过滤
- Using join buffer:多表join的次数非常多
- impossible where:where逻辑错误
八、索引优化
索引优化的总结
- 永远用小的结果集(主表)驱动大的结果集(驱动表)
- 优先优化内层的
- 保证join语句中被驱动表上的join条件字段被索引
九、索引失效
- 全职匹配:查询的语句的字段和顺序恰好和建立的索引的字段和顺序一致,否则索引会失效;
- 最佳左前缀法则:如果索引了多列,要遵守最左前缀法则(查询从索引的最左前列开始并且不能跳过索引中的列);
- 不在索引上做任何操作,包括计算、函数、(手动或自动)的类型转换,会导致索引失效,进而进行全表扫描;
- 不能使用索引中的范围条件右边的列;
- 减少使用select *,尽量使用索引的查询即索引列和查询列一致;
- 在使用不等于!= 、<> 的时候无法使用索引导致全表扫描;
- like以通配符开头(‘%abc’)时,索引会失效变成全表扫描; 解决方法可以使用覆盖索引,但是该方法具有一定的局限性;
- is null, is not null也无法使用索引;
- 字符串不加单引号会导致索引失效;
- 少用or,用它来连接时会失效;
十、查询优化
- 小表驱动大表
# 小表驱动大表 use myemployees; # 员工表有107条数据 select count(*) from employees; # 部门表有27条数据 select count(*) from departments; # 案例1. # 88ms select * from employees e where e.department_id in ( select d.department_id from departments d ); # 107ms select * from employees e where exists(select 1 from departments d where d.department_id = e.department_id); # 当d表的数据小于e表时,采用in要由于exists # in后面跟的是小表,exists后面跟的是大表(in后小,exists大) # select ...from table where exists(subquery)的理解:将主查询中的数据放入子查询中做条件验证,根据验证的结果(true/false)来决定主查询的数据是否保留
- order by关键字优order
原则:尽量使用Index方式排序,避免使用FileSort方式排序。链接一
filesort的原理,以及两种算法。链接二