作者回复: 执行1分钟主要是因为我的测试虚机IO性能太差了。 执行计划没有问题。 mysql> explain select count(*) from t_jointab t1, t_jointab t2 where t1.a = t2.a and t1.b = t2.b; +----+-------------+-------+------+---------------+-------+---------+----------+------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+------+---------------+-------+---------+----------+------+ | 1 | SIMPLE | t1 | ALL | idx_a | NULL | NULL | NULL | 8580 | | 1 | SIMPLE | t2 | ref | idx_a | idx_a | 4 | rep.t1.a | 1 | ### 虚机上执行 1分多 mysql> select count(*) from t_jointab t1, t_jointab t2 where t1.a = t2.a and t1.b = t2.b; +----------+ | count(*) | +----------+ | 10000 | +----------+ 1 row in set (1 min 30.77 sec) 换到笔记本上执行也是1秒多。 ### 笔记本上的执行时间 1.14秒 mysql> select count(*) from t_jointab t1 , t_jointab t2 where t1.a = t2.a and t1.b = t2.b; +----------+ | count(*) | +----------+ | 10000 | +----------+ 1 row in set (1.14 sec)
作者回复: 考虑得很全面了。 这个场景下使用hash join,或者将索引idx_a(a)改成组合索引idx_a(a,b) 都能取得不错的效果。👍👍 虽然这里的例子是人为构造的,但是正式环境中有时也会遇到类似的问题。有时也把这种情况称为“数据倾斜”。
作者回复: 先看下SQL的执行计划,是一个嵌套循环。 mysql> explain format=tree select count(*) from t_jointab t1 , t_jointab t2 where t1.a = t2.a and t1.b = t2.b\G *************************** 1. row *************************** EXPLAIN: -> Aggregate: count(0) (cost=4307.55 rows=1) -> Nested loop inner join (cost=4221.75 rows=858) -> Index scan on t1 using idx_ab (cost=1218.75 rows=8580) -> Filter: (t2.b = t1.b) (cost=0.25 rows=0.1) -> Index lookup on t2 using idx_a (a=t1.a) (cost=0.25 rows=1) 这个执行计划,用伪代表示,大概是这样的: def get_count(): cnt = 0 for t1_row in (select * from t_jointab t1): for t2_row in (select * from t_jointab t2 where a = t1_row.a and b = t1_row.b): cnt = cnt + 1 return cnt 由于t1.a = 9000的时候,使用索引idx_a(a)能匹配到1000行数据,而且有1000条t1.a = 9000的数据,因此循环的执行次数比较多。 叶明同学的留言中,给出了解决这个问题的两种有效的方法。