本文共 4131 字,大约阅读时间需要 13 分钟。
接上文,其他的一些Mysql对于查询的优化,或者Mysql的不足,我们应该注意怎样优化。
Mysql的子查询实现比较有问题, 特别是对于IN(子查询),这样的方式。
比如:查询一个订单表中,所有支付方式为4也就是京东网银钱包的订单:EXPLAIN SELECT * FROM virtual_order vo WHERE vo.jd_order_id IN (SELECT jd_order_id FROM biz_pay_task p WHERE p.pay_type=4);
// 执行结果:
+—-+——————–+——-+—————–+—————+————-+———+——+——+————-+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +—-+——————–+——-+—————–+—————+————-+———+——+——+————-+ | 1 | PRIMARY | vo | ALL | NULL | NULL | NULL | NULL | 50 | Using where | | 2 | DEPENDENT SUBQUERY | p | unique_subquery | jd_order_id | jd_order_id | 8 | func | 1 | Using where | +—-+——————–+——-+—————–+—————+————-+———+——+——+————-+ 实际上,上面的sql会被转化为:SELECT * FROM virtual_order vo WHERE EXISTS (SELECT jd_order_id FROM biz_pay_task p WHERE p.pay_type=4 AND vo.jd_order_id=p.jd_order_id);
这意味着,先对vo进行了全表扫描,然后再对每一个jd_order_id去执行子查询,看是否返回true
EXISTS关键字决定了 其子查询是否能够有返回结果。这种情况下,我们期望怎样执行呢?当然是先执行子查询,有结果了之后利用IN的二分查找来加快检索速度。
这样的话,我们就可以使用GROUP_CONCAT()来手工拼一个IN里面的内容, 或者改写为如下的内连接:SELECT * FROM virtual_order vo INNER JOIN biz_pay_task p USING(jd_order_id) WHERE p.pay_type=4 ;
但是关联子查询并不是一定比左连,内联等慢的。
比如NOT EXISTS的情况下,会有终止返回的优化,还是有可能变得比较快。 针对于这种,最好的方式就是进行测试,得到实际的执行结果,来进行判断。(SQL1) UNION ALL (SQL2) LIMIT 20;
这种情况会把 SQL1和SQL2中的所有记录放到临时表中然后去LIMIT20.比较好的做法是。分别LIMIT 如下: (SQL1 LIMIT 20) UNION ALL (SQL2 LIMIT 20) LIMIT 20;当遇到ON, USING的时候会诱发等值传递的优化,但是如果要传递的是IN(),或者其他比较复杂的等值时,有可能传递给内层之后影响内层的性能。
可以使用自建哈希字段加索引
也就是跳着扫描。比如有一个聚合索引(a, b)如果WHERE中只有b的条件,那么因为用不到这个索引,就会变成全表扫描。 我们期望的是先从a的索引开始扫描,然后对于a的没一个索引键中因为b是有序的,因此这个时候又可以对于每个键中的b的区间范围进行索引扫描。比如如下的结构:
|a|b| |1|1| |1|2| |1|3| |1|4| |2|1| |2|2| |2|3| |2|4| WHERE b between 2 and 3 扫描方式变为a1-b2-b3-a2-b2-b3,这样跳着扫描可以减少扫描行数,大幅度增加性能。 Mysql的主要应用掉在于GROUP BY 的语句中。Extra字段显示”Using index for group-by” 这里有一个更好的说明:SELECT MIN(id) from user WHERE name=”wzj”;
这里会做全表扫描。 但是如果是从主键开始扫描的话,可以快很多。Mysql自己不能完成这种行为,但是我们可以手工的完成它,比如使用如下的语句: SELECT id FROM user USE INDEX(PRIMARY) WHERE name=”wzj” LIMIT 1;主要是认为的控制一下Mysql的优化过程,列举一些,更具体的需要看Mysql的官方文档了。
尽量用关联代替
Mysql5.6中已经自带了这种优化,因此可以不是很关注了确定只有必须要去除重复行的时候采用UNION,否则使用UNION ALL,免得对所有结果进行DISTINCT
Percona 的pt-query-advisor
mysql并不是最好的选择,一般会有如下两个问题要解决:
BEGIN;SELECT id FROM task WHERE owner = 0 AND status=0 LIMIT 10 FOR UPDATE;-- result 1,2// 代码中dosomething.UPDATE task SET status=1 , owner = CONNECTON_ID() WHERE id IN (--result);COMMIT;
这样会造成使用相同索引的相同查询有锁定, 可以使用如下的方式代替:
BEGTIN;UPDATE task set status=1 , owner=CONNECTION_ID() WHERE owner = 0 AND status=0 LIMIT 10;COMMIT;SET AUTOCOMMIT = 0;SELECT id FROM task where owner = CONNECTION_ID() AND status = 1;// do something
这种可以大幅的减少锁的时间,但是后续的处理可能会比较多,比如如果执行具体的事情失败,则还需要回写内容。
上面的这种也稍显复杂,最好还是使用悲观锁。或者是直接使用redis,是一个更好的队列容器。
这个主要是需要注意,什么情况下使用索引。显然转化为矩形的情况下会使用索引。 使用矩形得出晓得数据集之后再进行精确计算。
转载地址:http://nakja.baihongyu.com/