今天碰到一个数据库问题,需要用到子查询,但尝试了一下发现它很慢:
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
分享到:
相关推荐
MySQL性能优化是指通过调整MySQL数据库的配置、优化数据库结构和查询语句等方式,提高MySQL数据库的性能和响应速度,以满足应用程序的需求。MySQL作为最流行的开源数据库之一,被广泛应用于各种规模的企业和应用程序...
MySQL开发通常指的是使用MySQL数据库进行的软件开发活动。MySQL是一个流行的开源关系型数据库管理系统(RDBMS),广泛用于Web开发和...6. **性能优化**:开发者需要了解如何优化查询性能,包括使用索引、优化SQL语句等
本书详细描述了优化过程中的关键步骤和方法,例如:分析慢查询日志、制定优化计划、调整配置参数等。还介绍了作者在实践中积累的宝贵经验,为读者提供实用的参考。 经验教训 本书总结了在优化MySQL性能和高可用...
MySQL性能优化是一个涵盖广泛的主题,涉及多个层面,包括SQL语句优化、索引优化、数据库表结构优化、系统级配置优化以及服务器硬件优化。以下是对这些方面进行详细说明: 1. **SQL语句优化** - **慢查询日志**:...
1. MySQL性能监控:如pt-query-digest、Percona Monitoring and Management (PMM) 可以分析慢查询日志,找出性能瓶颈。 2. 系统资源监控:使用工具如top、iostat、vmstat、sysstat等监控CPU、内存、磁盘I/O和网络...
总结来说,MySQL的慢查询日志记录了那些执行时间超过预设阈值的SQL语句,通过适当配置和分析这些日志,可以有效发现并优化数据库中的性能瓶颈。在使用慢查询日志时,需注意它可能会对数据库性能造成一定影响,因此...
- **Extra**:MySQL解决查询的详细信息。 #### 四、优化数据库结构 1. **表设计**:合理设计表结构,避免冗余数据。 2. **索引管理**:为经常查询的字段建立索引,减少查询时间。 3. **分区技术**:使用分区技术...
### MySQL性能优化知识点详解 #### 一、背景及目标 - **目的**:厦门游家公司(4399.com)为了提升员工技能水平,制定了这份MySQL性能优化教程,旨在为已有一定MySQL使用经验的工程师提供实战指导。 - **适用场景*...
对于慢查询,可以通过调整查询语句,添加合适的索引,或者优化数据库配置(如增加缓冲池大小)来改善。 **理解执行状态**,通过监控MySQL的运行状态,我们可以发现潜在的性能瓶颈。**常见关注重点**包括连接数、锁...
MySQL 性能优化详解 ...MySQL 的性能优化是一个复杂的任务,需要从多方面考虑,包括数据库设计、查询优化、服务器端优化和存储引擎优化。只有通过合理的优化,才能提高 MySQL 的性能,满足实际应用的需求。
6. **慢查询日志分析**:通过记录并分析慢查询日志,可以找出需要优化的SQL语句。 7. **定期维护**:包括定期分析和优化表,清理无用数据,更新统计信息等,以保持数据库的良好运行状态。 总的来说,MySQL性能优化...
#### 一、MySQL优化概述 MySQL作为一种广泛使用的开源关系型数据库管理系统,在数据处理方面表现优秀。然而,在高并发、大数据量的情况下,可能会遇到性能瓶颈。因此,进行MySQL性能优化是非常必要的。MySQL优化...
6. MySQL 的性能调优:MySQL 的性能调优是通过优化查询语言的支持,高效的数据存取,简单易用,成本低廉等来实现的。 7. MySQL 的应用场景:MySQL 广泛应用于各个行业,例如,微型的嵌入式系统,小型的 web 网站,...
8. **慢查询日志**:开启慢查询日志,记录执行时间超过设定阈值的查询,以便找出并优化低效的SQL语句。 9. **定期分析与优化**:使用ANALYZE TABLE和OPTIMIZE TABLE命令定期分析和优化表,更新统计信息,确保查询...
7. **日志分析**:MySQL的日志(如慢查询日志和错误日志)是诊断问题的重要来源。该脚本会检查这些日志的启用状态和配置,确保问题能被及时记录和追踪。 使用`tuning-primer.sh`时,首先需要在MySQL服务器上下载并...
8. **慢查询日志**:记录执行时间超过设定阈值的查询,帮助识别性能问题。结合日志分析工具,可以找到需要优化的SQL语句。 9. **定期分析与优化**:使用ANALYZE TABLE收集统计信息,帮助优化器做出更好的执行计划。...
然而,在面对大规模数据集时,即使是性能强大的MySQL也可能遇到查询速度慢的问题。为了帮助数据库管理员、开发者、学生以及MySQL爱好者们突破性能瓶颈,《高效MySQL查询加速指南》提供了全面且实用的方法,帮助用户...