(1)回顾:
SELECT COUNT(id) FROM users WHERE id IN(SELECT user_id FROM users_extent_info WHERE latest_login_time
可能同样的SQL去不同的MySQL里没法还原出来这里的执行计划,重点学习执行计划的分析思路,以及如何从执行计划里看出性能问题所在,最后就是如何进行调优。
(2)执行计划分析:
EXPLAIN SELECT COUNT(id) FROM users WHERE id IN (SELECT user_id FROM users_extent_info WHERE latest_login_time < xxxx)
前景为了方便看,把执行计划简化了几个字段,保留最关键的几个字段,为了调优在测试环境的单表2万条数据场景下跑出来的执行计划,users表5万条,当时这个SQL跑了十多秒,所以足够复现问题。 MATERIALIZED :物化表
+----+-------------+-------+------------+-------+---------------+----------+---------+------+
| id | select_type | table | type | key | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+
| 1 | SIMPLE | <subquery2> | ALL | NULL | NULL | 100.00 | NULL |
| 1 | SIMPLE | users | ALL | NULL | 49651 | 10.00 | Using where; Using join buffer(Block Nested Loop) |
| 2 | MATERIALIZED | users_extent_info | range | idx_login_time | 4561 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+
首先针对子查询,是执行计划里的第三行实现的,针对users_extent_info,使用了 idx_login_time 这个索引,做了range类型的索引范围扫描,查出来4561条数据,没有其他额外的筛选,所以filtered是100%;
接着是MATERIALIZED,表明了这里把子查询的4561条数据代表的结果集进行了物化,物化成一个临时表,这个临时表物化,一定会把4561条数据临时落到磁盘文件里去,这个过程挺慢的。
然后第二条执行计划表明针对users表做了一个全表扫描,在全表扫描的时候扫出来49651条数据,同时Extra字段,显示一个Using join buffer 的信息,这个表示在执行join操作???
接着执行计划第一条,针对子查询查出的一个物化临时表,也就是
第二条执行计划的全表扫描的结果表明一共扫到了49651条数据,但是全表扫描过程中,因为跟物化临时表执行了一个join操作,而物化临时表就4561条数据,所以最终第二条执行计划的filtered显示的是100%,也就是,最终从users表里筛选出来4000多条数据。
(3)结尾:
这就是该条SQL语句的执行计划,不同MySQL版本可能不一样,甚至差别很大,所以没必要还原这个执行计划。主要看SQL执行计划的分析过程。