数据库查询语句调优录
SQL调优的三个方向:
- 数据库对象的设计
- 常规查询语句的编写注意事项
- 根据SQL的执行计划进行定向优化
1. 数据库对象优化
数据库对象的设计优劣直接决定了SQL语句执行性能的上限,如果设计不当会导致很多的性能问题.
1.1 字段类型的优化
- 字段类型尽量短
- 字段类型尽量高效: 整型>浮点型>字符串
- 字段类型的长度需要设计冗余,避免后期因业务变更导致需要更改数据库结构
1.2 表的拆分
影响表的查询效率的一个比较大的因素是数据量.
为了避免因数据量的增多而导致SQL语句执行缓慢,在表的设计初期会考虑表的拆分操作.一般表的拆分分为两种:
- 水平表拆分:分区-RANGE,LIST,HASH,KEY等.
- 垂直表拆分:根据业务逻辑将一张表中常用列和非常用列拆分出来,使得单条数据行变小,提高查询效率,但查询所有数据需要JOIN操作(使用join会使查询效率变低).
1.3 反范式操作
数据库表的范式设计能使得表之间的结构清晰,表之间的关系复杂,表之间的JOIN操作频繁,而JOIN操作属于性能较低的操作,会影响查询性能.因此对于一些JOIN查询较多的业务场景,需要根据实际情况采用反范式的方式来对数据库进行设计.
- 增加冗余列:在多个表中具有相同的列;
- 增加派生列:增加的列来自其他表数据或有其他表的数据计算生成,避免使用集函数;
- 重新生表: 用户经常那个需要查看多个表的JOIN结果,把这些表重新组成一个表;
- 分割表:表的拆分操作;
1.4 中间表设计
针对某些特定数据量比较大的表,用户需要先筛选然后做分析时,中间表的设计会使得查询效率变高.
- 中间表需要和源表结构完全相同.
- 一般先将需要统计的表数据转移到中间表上,然后再中间表上做统计
- 中间表的设计有如下优点:
- 与源表’隔离’,在中间表上做统计操作不会对源表产生影响
- 中间表可以灵活添加索引或新增临时的新字段从而提高查询效率和辅助统计查询.
1.5 索引设计
索引建立和使用规则
- 索引字段尽量小;
- 最左前匹配原则;
- =和in可以乱序;
- 尽量选择区分度高的列作为索引列
- 索引列不能参与计算,保持列’干净’
- 尽量地扩展索引,不要新建索引
索引失效的情况:
- 组合索引不遵循最左匹配原则
(存在组合索引(age,name) 则age=1, age=1 and name= ‘jia’ , name=‘jia’ and age=1 三种情况下索引生效,单一个 name=‘jia’ 不满足最左匹配,会导致索引失效 ) - 组合索引的前面索引列使用范围查询(<.>,like),会导致后续的索引失效
- 不要在索引上做任何操作(计算,函数,类型转换)
- is null和is not null无法使用索引
- 尽量少使用or操作符,否则连接时索引会失效
- 字符串不添加引号会导致索引失效
- 两表关联使用的条件字段中字段的长度、编码不一致会导致索引失效
- like语句中,以%开头的模糊查询
- 如果mysql中使用全表扫描比使用索引快,也会导致索引失效
2 常见的SQL语句调优
- 当使用INSERT插入多行记录时,使用INSERT INTO table_name VALUES () , (),…();
- SELECT语句尽量不要使用*
- 尽量少使用GROUP BY,一般建议通过关联查询和DISTINCT代替
- GROUP BY非要用的话,在分组字段上添加索引
- 查询一条记录时,使用 LIMIT 1.
- 灵活的使用EXISTS和IN,NOT EXISTS和NOT IN:
- EXISTS和IN:有两个待查询的表,当两表数据量差别不大时,使用起来差别不大;若两个表一大一小,则子查询大表时,推荐使用EXISTS;子查询小表时,推荐使用IN,例:
- A表>B表:
select * from A where id in (select id from B);
- A表<B表:
select * from A where exists ( select 1 from B where B.id=A.id);
- A表>B表:
- NOT EXISTS和NOT IN: 用NOT EXISTS总是比NOT IN效率高
- EXISTS和IN:有两个待查询的表,当两表数据量差别不大时,使用起来差别不大;若两个表一大一小,则子查询大表时,推荐使用EXISTS;子查询小表时,推荐使用IN,例:
- 多用函数,根据业务需求搭配合适的函数,减少数据再转换
- 大量数据插入时,先删除索引及约束并关闭自动提交,等数据插入完成后,再重新创建索引和约束
- 适当添加索引
- 不提倡使用JOIN,但若不得不用时,需要注意使用JOIN时要以小表去驱动大表.例:小表 left join 大表
3. 根据EXPLAIN的SQL执行计划进行SQL调优
3.1 调优步骤
-
查看type类型为All的计划(即进行全表扫描的SQL).优化查询逻辑
-
查看extra为Using where的计划,进行索引向的优化.
-
对于创建了索引但未生效的计划,可以使用Hint语法强制使用索引(需要适配使用索引的原则)
SELECT 某字段 FROM 某表 FORCE INDEX(索引名) WHERE … -
虽然不提倡使用JOIN,但若不得不用时,需要注意使用JOIN时要以小表去驱动大表.
小表 left join 大表; 大表 right join 小表; 小表 straight_join 大表(功能同join类似,但能让左边的表来驱动右边的表);
3.2 关于explain
- id:SQL执行的顺序的标识,Id越大优先级越高,相同Id则从上到下执行
- select_type:
- SIMPALE:简单select,不适用union或子查询等
- PRIMARY: 子查询中最外层查询,查询中若包含子查询,最外层的select被标记为primary.
- UNION: union中的第二个或后面的select语句.
- DEPENDENT UNION: union中的第二个或后面的select语句.
- UNION RESULT: UNION的结果.
- SUBQUERY: 子查询中的第一个select,结果不依赖于外部查询.
- DEPENDENT SUBQUERY: 子查询中的第一个select,依赖于外部查询.
- DERIVED: 派生表的select, FROM子句的子查询
- UNCACHEABLE SUBQUERY: 一个子查询的结果不能被缓存,必须重新评估外链接的第一行
- table: 访问的数据库中表的名称,有时不是真实的表的名字,可能是简称
- type: 对表的访问方式,表示在表中找到所需行的方式,又称’访问类型’.从上到下,性能越来越好
- ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行.
- index: Full Index Scan,index与ALL区别为index类型只遍历索引树.
- range:只检索给定范围的行,使用一个索引来选择行.
- Index_range:表示使用了索引合并的优化方法.
- Ref_or_null: 类似ref,可以搜索值为NULL的行.
- Ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值.
- eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件.
- const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system.
- NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成.
- possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用.
- key: 显示MySQL实际决定使用的键(索引) .如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX.
- key_len: 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)不损失精确性的情况下,长度越短越好.
- Ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值.
- Rows 表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数.
- Extra :解决查询的详细信息,如果出现下面的Using temporary和Using filesort说明查询效率低
- Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤.
- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询.
- Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”.
- Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果.如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能.
- Impossible where:这个值强调了where语句会导致没有符合条件的行.
- Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
- No table used: Query语句中使用from dual 或不含任何from子句.
EXPLAIN总结
- EXPLAIN无法获取触发器,存储过程以及用户自定义函数对查询的影响
- EXPLAIN不考虑Cache
- EXPLAIN无法显示数据库在执行查询时做的优化工作
- EXPALIN产生的统计信息是估算的,并非精确
- EXPALIN一般针对SELECT语句,其他操作要重写为select后查看执行计划