kxbin
成功留给有准备的人
posts - 10,  comments - 35,  trackbacks - 0

mysql> select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');

为了节省篇幅,省略了输出内容,下同。

67 rows in set (12.00 sec)


只有67行数据返回,却花了12秒,而系统中可能同时会有很多这样的查询,系统肯定扛不住。用desc看一下(注:explain也可)


mysql> desc select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839'); 
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
| id | select_type        | table            | type   | possible_keys   | key   | key_len | ref        | rows    | Extra                    |
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
|  1 | PRIMARY            | abc_number_prop  | ALL    | NULL            | NULL  | NULL    | NULL       | 2679838 | Using where              |
|  2 | DEPENDENT SUBQUERY | abc_number_phone | eq_ref | phone,number_id | phone | 70      | const,func |       1 | Using where; Using index |
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
2 rows in set (0.00 sec)


从上面的信息可以看出,在执行此查询时会扫描两百多万行,难道是没有创建索引吗,看一下


mysql> show index from abc_number_phone; 
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table            | Non_unique | Key_name    | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| abc_number_phone |          0 | PRIMARY     |            1 | number_phone_id | A         |       36879 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_phone |          0 | phone       |            1 | phone           | A         |       36879 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_phone |          0 | phone       |            2 | number_id       | A         |       36879 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_phone |          1 | number_id   |            1 | number_id       | A         |       36879 |     NULL | NULL   |      | BTREE      |         |               | 
| abc_number_phone |          1 | created_by  |            1 | created_by      | A         |       36879 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_phone |          1 | modified_by |            1 | modified_by     | A         |       36879 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.06 sec)

mysql> show index from abc_number_prop; 
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table           | Non_unique | Key_name    | Seq_in_index | Column_name    | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| abc_number_prop |          0 | PRIMARY     |            1 | number_prop_id | A         |      311268 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_prop |          1 | number_id   |            1 | number_id      | A         |      311268 |     NULL | NULL   |      | BTREE      |         |               | 
| abc_number_prop |          1 | created_by  |            1 | created_by     | A         |      311268 |     NULL | NULL   |      | BTREE      |         |               |
| abc_number_prop |          1 | modified_by |            1 | modified_by    | A         |      311268 |     NULL | NULL   | YES  | BTREE      |         |               |
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.15 sec)


从上面的输出可以看出,这两张表在number_id字段上创建了索引的。


看看子查询本身有没有问题。

mysql> desc select number_id from abc_number_phone where phone = '82306839'; 
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
| id | select_type | table            | type | possible_keys | key   | key_len | ref   | rows | Extra                    |
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | abc_number_phone | ref  | phone         | phone | 66      | const |    6 | Using where; Using index |
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)


没有问题,只需要扫描几行数据,索引起作用了。查询出来看看

mysql> select number_id from abc_number_phone where phone = '82306839'; 
+-----------+
| number_id |
+-----------+
|      8585 |
|     10720 |
|    148644 |
|    151307 |
|    170691 |
|    221897 |
+-----------+
6 rows in set (0.00 sec)


直接把子查询得到的数据放到上面的查询中

mysql> select * from abc_number_prop where number_id in (8585, 10720, 148644, 151307, 170691, 221897);

67 rows in set (0.03 sec)


速度也快,看来MySQL在处理子查询的时候是不够好。我在MySQL 5.1.42 和 MySQL 5.5.19 都进行了尝试,都有这个问题。

搜索了一下网络,发现很多人都遇到过这个问题:

参考资料1:使用连接(JOIN)来代替子查询(Sub-Queries) mysql优化系列记录
http://blog.csdn.net/hongsejiaozhu/article/details/1876181
参考资料2:网站开发日记(14)-MYSQL子查询和嵌套查询优化
http://dodomail.iteye.com/blog/250199

根据网上这些资料的建议,改用join来试试。

修改前:select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');

修改后:select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839';

mysql> select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839';

67 rows in set (0.00 sec)

效果不错,查询所用时间几乎为0。看一下MySQL是怎么执行这个查询的

mysql> desc select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839'; 
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys   | key       | key_len | ref             | rows | Extra                    |
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
|  1 | SIMPLE      | b     | ref  | phone,number_id | phone     | 66      | const           |    6 | Using where; Using index |
|  1 | SIMPLE      | a     | ref  | number_id       | number_id | 4       | eap.b.number_id |    |                          |
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
2 rows in set (0.00 sec)

小结:当子查询速度慢时,可用JOIN来改写一下该查询来进行优化。

网上也有文章说,使用JOIN语句的查询不一定总比使用子查询的语句快。

参考资料3:改变了对Mysql子查询的看法
http://hi.baidu.com/yzx110/blog/item/e694f536f92075360b55a92b.html

正好手头有本《高性能MySQL》,翻阅了一下,第4.4节“MySQL查询优化器的限制”4.4.1小节“关联子查询”正好讲到这个问题。

MySQL有时优化子查询很差,特别是在WHERE从句中的IN()子查询。像上面我碰到的情况,其实我的想法是MySQL会把

select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');

变成下面的样子

select * from abc_number_prop where number_id in (8585, 10720, 148644, 151307, 170691, 221897);

但不幸的是,实际情况正好相反。MySQL试图让它和外面的表产生联系来“帮助”优化查询,它认为下面的exists形式更有效率

select * from abc_number_prop where exists (select * from abc_number_phone where phone = '82306839' and number_id = abc_number_prop.number_id);


mysql> select * from abc_number_prop where exists (select * from abc_number_phone where phone = '82306839' and number_id = abc_number_prop.number_id);

67 rows in set (10.89 sec)


mysql> desc select * from abc_number_prop where exists (select * from abc_number_phone where phone = '82306839' and number_id = abc_number_prop.number_id); 
+----+--------------------+------------------+--------+-----------------+-------+---------+-------------------------------------+---------+--------------------------+
| id | select_type        | table            | type   | possible_keys   | key   | key_len | ref                                 | rows    | Extra                    |
+----+--------------------+------------------+--------+-----------------+-------+---------+-------------------------------------+---------+--------------------------+
|  1 | PRIMARY            | abc_number_prop  | ALL    | NULL            | NULL  | NULL    | NULL                                |2660707 | Using where              |
|  2 | DEPENDENT SUBQUERY | abc_number_phone | eq_ref | phone,number_id | phone | 70      | const,eap.abc_number_prop.number_id |       | Using where; Using index |
+----+--------------------+------------------+--------+-----------------+-------+---------+-------------------------------------+---------+--------------------------+
2 rows in set (0.01 sec)

这种in子查询的形式,在外部表(比如上面的abc_number_prop)数据量较大的时候效率是很差的。(如果对于较小的表,不会造成显著地影响)

文中说到一种优化方式就是,手工将in里面的子查询查询出来,然后再拼装执行 ,这在程序中是可行的。

posted on 2012-03-10 22:17 kxbin 阅读(252) 评论(0)  编辑  收藏 所属分类: MYSQL

只有注册用户登录后才能发表评论。


网站导航:
 
你恨一个人是因为你爱他;你喜欢一个人,是因为他身上有你没有的;你讨厌一个人是因为他身上有你有的东西;你经常在别人面前批评某人,其实潜意识中是想接近他。

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(5)

随笔档案

文章分类

文章档案

相册

收藏夹

J2EE

java技术网站

Linux

平时常去的网站

数据库

电影网站

网站设计

搜索

  •  

最新评论

阅读排行榜

评论排行榜