`

MySQL性能优化小记:MySQL子查询很慢的问题

阅读更多

今天碰到一个数据库问题,需要用到子查询,但尝试了一下发现它很慢:


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 |    3 |                          |
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
2 rows in set (0.00 sec)


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


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

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


本文链接:http://codingstandards.iteye.com/blog/1344833


正好手头有本《高性能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 |       1 | Using where; Using index |
+----+--------------------+------------------+--------+-----------------+-------+---------+-------------------------------------+---------+--------------------------+
2 rows in set (0.01 sec)


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


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


文中说到:但是总是认为子查询效率很差也是不对的,有时候可能子查询更好些。怎么确定这个事情呢,应该经过评测来决定(执行查询、用desc/explain等来看)。


在网上也能找到《高性能MySQL》的这节内容

参考资料4:MySQL 数据库优化(12)Limitations of the MySQL Query Optimizer
http://www.chenyajun.com/2009/01/03/1657




6
0
分享到:
评论
1 楼 miaojihao 2014-07-16  
说的很有道理

相关推荐

    千金良方:MySQL性能优化金字塔法则.docx

    MySQL性能优化是指通过调整MySQL数据库的配置、优化数据库结构和查询语句等方式,提高MySQL数据库的性能和响应速度,以满足应用程序的需求。MySQL作为最流行的开源数据库之一,被广泛应用于各种规模的企业和应用程序...

    MySQL性能优化和高可用架构实践.pptx

    本书详细描述了优化过程中的关键步骤和方法,例如:分析慢查询日志、制定优化计划、调整配置参数等。还介绍了作者在实践中积累的宝贵经验,为读者提供实用的参考。 经验教训 本书总结了在优化MySQL性能和高可用...

    mysql 性能优化与架构设计(word版)

    1. MySQL性能监控:如pt-query-digest、Percona Monitoring and Management (PMM) 可以分析慢查询日志,找出性能瓶颈。 2. 系统资源监控:使用工具如top、iostat、vmstat、sysstat等监控CPU、内存、磁盘I/O和网络...

    MySQL优化篇:慢查询日志.pdf

    总结来说,MySQL的慢查询日志记录了那些执行时间超过预设阈值的SQL语句,通过适当配置和分析这些日志,可以有效发现并优化数据库中的性能瓶颈。在使用慢查询日志时,需注意它可能会对数据库性能造成一定影响,因此...

    mysql性能的优化

    - **Extra**:MySQL解决查询的详细信息。 #### 四、优化数据库结构 1. **表设计**:合理设计表结构,避免冗余数据。 2. **索引管理**:为经常查询的字段建立索引,减少查询时间。 3. **分区技术**:使用分区技术...

    mysql性能优化教程.pdf (by caoz)

    ### MySQL性能优化知识点详解 #### 一、背景及目标 - **目的**:厦门游家公司(4399.com)为了提升员工技能水平,制定了这份MySQL性能优化教程,旨在为已有一定MySQL使用经验的工程师提供实战指导。 - **适用场景*...

    Mysql性能优化教程

    对于慢查询,可以通过调整查询语句,添加合适的索引,或者优化数据库配置(如增加缓冲池大小)来改善。 **理解执行状态**,通过监控MySQL的运行状态,我们可以发现潜在的性能瓶颈。**常见关注重点**包括连接数、锁...

    百度mysql性能优化ppt

    【标题】:“百度mysql性能优化ppt”所涉及的知识点涵盖了MySQL数据库在性能调优方面的多个重要环节。在MySQL性能优化中,我们关注的核心是提升数据处理速度,减少资源消耗,从而提高系统的整体效率。 【描述】:...

    mysql性能优化.pptx

    MySQL性能优化是一个涵盖广泛的主题,涉及多个层面,包括SQL语句优化、索引优化、数据库表结构优化、系统级配置优化以及服务器硬件优化。以下是对这些方面进行详细说明: 1. **SQL语句优化** - **慢查询日志**:...

    mysql性能优化与架构设计

    6. **慢查询日志分析**:通过记录并分析慢查询日志,可以找出需要优化的SQL语句。 7. **定期维护**:包括定期分析和优化表,清理无用数据,更新统计信息等,以保持数据库的良好运行状态。 总的来说,MySQL性能优化...

    MySql性能优化集合--满满的干货

    #### 一、MySQL优化概述 MySQL作为一种广泛使用的开源关系型数据库管理系统,在数据处理方面表现优秀。然而,在高并发、大数据量的情况下,可能会遇到性能瓶颈。因此,进行MySQL性能优化是非常必要的。MySQL优化...

    大牛-高级教程MySQL性能优化实战

    8. **慢查询日志**:开启慢查询日志,记录执行时间超过设定阈值的查询,以便找出并优化低效的SQL语句。 9. **定期分析与优化**:使用ANALYZE TABLE和OPTIMIZE TABLE命令定期分析和优化表,更新统计信息,确保查询...

    MySQL性能优化诊断脚本

    7. **日志分析**:MySQL的日志(如慢查询日志和错误日志)是诊断问题的重要来源。该脚本会检查这些日志的启用状态和配置,确保问题能被及时记录和追踪。 使用`tuning-primer.sh`时,首先需要在MySQL服务器上下载并...

    MySQL性能优化教学视频资料.zip

    8. **慢查询日志**:记录执行时间超过设定阈值的查询,帮助识别性能问题。结合日志分析工具,可以找到需要优化的SQL语句。 9. **定期分析与优化**:使用ANALYZE TABLE收集统计信息,帮助优化器做出更好的执行计划。...

    高效MySQL查询加速指南:索引策略、查询优化、性能调优,助力数据库管理员和开发者突破性能瓶颈

    然而,在面对大规模数据集时,即使是性能强大的MySQL也可能遇到查询速度慢的问题。为了帮助数据库管理员、开发者、学生以及MySQL爱好者们突破性能瓶颈,《高效MySQL查询加速指南》提供了全面且实用的方法,帮助用户...

    mysql性能优化-慢查询分析、优化索引和配置.doc

    MySQL作为一种广泛使用的开源关系型数据库管理系统,在很多应用场景中都可能遇到性能瓶颈的问题。常见的性能瓶颈主要分为两大类:**CPU瓶颈**和**I/O瓶颈**。 - **CPU瓶颈**:通常发生在处理大量数据时,尤其是在将...

Global site tag (gtag.js) - Google Analytics