高性能的MySQL(6)优化特定类型的查询

时间:2022-10-21 19:40:38

只要说明几种日常用到的查询可以优化的地方,只是特别的情况下可以使用,不要期望任何情况下都成立,优化是一个实践的漫长过程!

一、最大值和最小值优化

对于min()和max()查询,MySQL的优化并不是很好,比方说:

select min(actor_id) from actor where first_name = 'Jane';

因为first_name上没有索引,所以这个会扫描全表。如果MySQL能按主键扫描,那么第一个满足条件的记录就是要找的最小指了,因为主键是严格按照大小顺序排列的,一个可以尝试的方法是不用min(),使用limit:

select actor_id from actor use index(primary) where first_name = 'Jane' limit 1;

但是这条SQL的本意就不明显了。

二、在同一个表上查询和更新

MySQL不允许对同一张表同时进行查询和更新,比方说

update tab1 as outer_tabset cnt = (select count(*) from tab1 as inner_tabwhere inner_tab.type = outer_tab.type);

可以使用生成表的形式绕过上面的限制,子查询会在update语句打开之前就完成。

update tab1inner join(select type,count(*) as cnt from tab1 group by type)as der using(type)set tab1.cnt = der.cnt;

三、使用优化器提示(hint)

这里之说明几个重要点的

1、DELAYED

这个提示对insert 和 replace有效。MySQL会将使用该提示的语句立即返回给客户端,并将插入的数据放到缓冲区,然后在表空闲时批量将数据写入。特别适合需要写入大量数据但是客户端却不需要等待语句完成I/O的应用。

2、STRAIGHT_JOIN

这个提示可以放在select语句的select关键字之后,也可以放在任何两个关联表的名字之间。第一个用法是让查询中所有的表按照在语句中出现的顺序进行关联。第二个用法则是固定前后两个表的关联顺序。当你可以确定某个关联顺序最佳的时候,可以使用,但是版本升级的时候要重新检查该类查询。

3、SQL_SMALL_RESULT 和 SQL_BIG_RESULT

这个提示只针对select语句有效。它告诉优化器对group by 或者 distinct查询如何使用临时表及排序。

4、SQL_BUFFER_RESULT

这个提示告诉优化器将查询结果放到一个临时表,然后尽可能快的释放表锁。

5、SQL_CACHE 和 SQL_NO_CACHE

这个提示告诉MySQL结果集是否应该缓存在查询缓存中。

6、FOR UPDATE 和 LOCK IN SHARE MODE

控制行锁,只针对InnoDB引擎,这个具体以后会有介绍。

7、USE INDEX、IGNORE INDEX 和 FORCE INDEX

使用或者不使用索引来查询记录。


四、count()查询

count()有两种非常不同的作用:它可以统计某个列值的数量,也可以统计行数,统计列值要求列值是非空的(不统计NULL)。如果括号从指定了列或者列的表达式,则统计的就是这个表达式有值的结果数。当MySQL确认括号中表达式不可能为空的时候,实际上就是统计行数。

MyISAM的count()只有在没有任何where条件的时候才会很快。

我们恰恰可以利用这一点来在特定条件下优化比方说:我们在一个有500W记录的表里执行语句

#id从10000递增select count(*) from emp where id > 10005;

和我们利用如下方法来比较时间上的差异

select (select count(*) from emp) - count(*) from emp where id <= 10005;

高性能的MySQL(6)优化特定类型的查询

如何在同一个查询中统计同一个列的不同值的数量,以减少查询的语句量。

可以有如下两种方式:

#1select sum(if(color='blue',1,0)) as blue,sum(if(color='red',1,0)) as red from tab;#2select count(color='blue' or null) as blue,count(color='red' or null) as red from tab;

五、优化GROUP BY 和 DISTINCT

如果需要对关联查询分组,并且是按照查找表中的某个列进行分组,那么通常采用查找表的标识列效率会比其他列更高。

例如下面的查询,效果不会很好

select actor.first_name,actor.last_name,count(*) from film_actorinner join actor using(actor_id)group by actor.first_name,actor.last_name;

如果这样写效率会更高

select actor.first_name,actor.last_name,count(*) from film_actorinner join actor using(actor_id)group by actor.id;

如果没有通过order by子句显示的指定排序列,当查询使用group by子句的时候,结果会自动按照分组的字段进行排序,如果不关心排序,可以使用order by null避免排序,节省时间。

六、优化LIMIT分页

如果偏移量很大的时候,这样的查询会很慢,前面的文章我们已经测试过了,也提供了一种解决的办法,那就是“延迟关联”。

当然我们还可以记录上一页的id最大值,然后在查询下一页的时候用这个值来作为条件,但是这样只有能按下一页查找,不能直接跳转了。

七、优化UNION

Mysql总是通过创建并填充临时表的方式来执行union查询,除非确实需要服务器消除重复的行,否这一定要使用union all,这一点很重要。如果没有all,Mysql会给临时表加上distinct,这会导致整个脸时表的数据唯一行检查,代价很高。

八、使用用户自定义变量

如果在更新的同时又希望获得该行的信息,我们能怎么做?

比方说如下代码:

update t1 set update_time = now() where id = 1;select update_time from t1 where id = 1;

我们可以这样做:

update t1 set update_time = now where id = 1 and @now := now();select @now;

虽然也是两条语句,但第二条语句没有查询表,耗时可以忽略了!






本文出自 “phper-每天一点点~” 博客,请务必保留此出处http://janephp.blog.51cto.com/4439680/1316903