数据库优化之SQL查询优化

SQL查询优化:




1 MySQL查询路径
MySQL查询路径如下图所示:




查询的生命周期大致包括:从客户端 到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。

1.1 MySQL客户端/服务器通信协议
MySQL客户端和服务器端的通信协议是"半双工"的,也就是说在任何一个时刻,要么由服务器向客户端发送数据,要么由客户端向服务器发送数据,这两者不可能同时发生,有你没我,有我没你。

1.2 查询缓存
如果缓存命中,则直接返回。
1.3查询优化处理
MySQL会对SQL语句进行语法验证,并将SQL语句生成一个"解析树"。
MySQL能够处理的优化有:
1)重新定义关联表的顺序。MySQL采用嵌套循环的方式处理表关联,所以小表驱动大表性能更好。
2)使用等价变化规则。例如5=5 and a>5就会被改写为a>5;
3) 优化max()、min()。如果有索引B树最左边的值就是最小值,最右边的值就是最大值。
4)预估并转换成常量表达式。



例如对于这个查询,先查询film表,因为film_id是主键,所以type=const,然后将film_id作为已知的值,去film_actor表中查询。这个走索引因为type=ref。

5)提前终止查询。比如limit 、not exists()等。
6)IN()。MySQL会将in中的数据进行排序,然后通过二分查找法确定列表中的值,所以in中数据量大的时候,不等价于or,比转换成or性能会好一些。


1.4 查询执行引擎
根据生成的执行计划,调用存储引擎提供的接口来完成。

1.5 返回给客户端
MySQL将结果集返回客户端是一个增量、逐步返回的过程,这样服务器端无需存储太多的结果,另外客户端也可以第一时间获取结果。


2 为什么查询会慢?
查询可以看做是由一系列子任务组成,每个子任务都会消耗一定的时间。优化查询实际就是要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数,要么让子任务执行的更快。

2.1 访问了太多的行,或者访问了太多的列
a.查询不需要的记录。例如对于大表查询没有做分页,或者只做了前台分页,没做后台分页,这将导致查询了n多记录,但是只用了很少的一部分,白白消耗了数据库和网络的能量,没用到正地方,这是一个倡导绿色,节约的社会,这样未免太不和谐了。

b.访问了太多的列。比如喜欢用select * ,尤其多表关联的时候,要注意select *,应该只返回有价值的列。或者表中有大字段,但是这个查询并用不到大字段的时候,也要注意过滤掉大字段,查询大字段消耗更多。
当然凡事有个度,这个原则也别被滥用,比如select col1,col2,col3 from tbl where id=1;然后把col1,col2,col3缓存起来,就要好过col1,col2,col3分3次查询。

c.使用缓存。
对于读多写少等存在热点数据的场景,通常使用缓存,将这些数据缓存起来,可以使用本地缓存或者memcached等分布式缓存,避免数据库查询。

d.避免太复杂的SQL语句。
笔者遇到的项目中有几个长SQL,长度都超过了满满1页屏幕,各种嵌套,各种子查询,笔者实在是佩服开发者的水平,反正偶觉得一般人是写不出来滴。其实遇到这种情况,我觉得一定不要硬撑下去,这个一定是数据库设计出了问题,或者一定要考虑把大SQL做拆分,分而治之。



2.2 MySQL服务器在分析大量超过需要的数据行

访问类型(按速度由慢到快排序):全表扫描,范围扫描,索引扫描,常数引用等。

create table tbl(col1 int not null primary key,col2 varchar(10) not null,col3 varchar(10) not null);
CREATE  INDEX idx_tbl_col2 ON tbl (col2);

a.explain select * from tbl where col1=1;
type=const //因为col1是主键,所以类型为const,这种效率最高
b.explain select * from tbl  where col2='1';
type=ref //因为col2字段上有索引,所以type的类型是ref,表示按索引扫描,如果是覆盖索引,extra中还会显示using index
例如explain select col2 from tbl where col2='1'; 这种查询不需要回表,所以性能也是杠杠的。
c.explain select * from tbl  where col2 between '1' and '5';
type=range //range表示是索引范围扫描
d.explain select * from tbl where col3='1';
type=all ;//由于col3未建索引,所以全表扫描,这对于大表是遭难性的。这个虽然可能返回了很少的数据行,但是为了找到这个数据行,却扫描了整张表。


3 优化特定类型的查询
3.1优化COUNT()查询
1)不要用count(字段),用count(*)
2) MyISAM存储引擎如果没有where条件,那么他会直接获取出,如果有where条件那么他跟其他存储引擎速度一样
3) 分别查询红颜色和蓝颜色的语句,可以写成:
select count(color='red' or null) as red ,count(color='blue' or null) as blue from items;
4)可以使用近视值的时候可以通过explain获取,explain并不实际执行,获取去掉一些精确性的约束。
5)使用汇总表。
总之:快速,精确和实现简单,三者只能取其二。

3.2 优化关联查询
1)确保ON和USING子句中的字段有索引。
2)确保GROUP BY 和 ORDER BY的表达式只用到一个表中的列,这样MySQL才能使用索引做优化。

3.3 优化LIMIT分页
首先要确保相关字段有索引,另外如果偏移量非常大,可以考虑使用子查询。
例如:
select film_id ,description from film order by title limit 10000,20;
可以改写为:
select film_id, description from film inner join (
select film_id from film order by title limit 1000,20
)as lim using(film_id);

内部子查询会使用索引覆盖,获取到值以后,再回表检索数据,这样使MySQL访问了尽可能少的页。

3.4 优化UNION
1)MySQL总是通过临时表的方式执行UNION查询
2)需要将where ,limit等子句手工的下推到各个union的子查询中。
3)除非必要,尽可能使用UNION ALL



《高性能MySQL》

猜你喜欢

转载自frank1234.iteye.com/blog/2167135