`
pany
  • 浏览: 90313 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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

分享到:
评论

相关推荐

    phpstudy Apache PHP MySQL phpMyAdmin ZendOptimizer

    该程序转载他人,希望帮学习PHP的朋友有所帮助。我找了很久,这个东西。...注意:安装过程中,如有防火墙开启,注册启动服务时,会提示是否信任httpd、mysqld-nt运行,以及端口80、3306等,请选择允许。

    MYSQL培训经典教程(共两部分) 1/2

    数据库优化 177 8.1 索引的使用 178 8.1.1索引对单个表查询的影响 ...过程检查表列 186 8.2.3 总结 187 8.3 SQL查询的优化 187 8.3.1 使用EXPLAIN语句检查SQL语句 187 8.3.2 SELECT 查询的速度 188...

    MYSQL培训经典教程(共两部分) 2/2

    数据库优化 177 8.1 索引的使用 178 8.1.1索引对单个表查询的影响 ...过程检查表列 186 8.2.3 总结 187 8.3 SQL查询的优化 187 8.3.1 使用EXPLAIN语句检查SQL语句 187 8.3.2 SELECT 查询的速度 188...

    程序设计说明书(转载)

    优化过程可能包括调整表的索引、分区策略,或者改进数据的访问方式,以提高查询效率。 总的来说,程序设计说明书详尽地描述了数据库的设计过程,包括其目标、背景、使用的技术、设计原则以及实体和关系。这样的文档...

    本人提供SQL语句大全(转载) 12009年04月28日 星期二 19:35SQL语句大全(转载)

    - **SELECT TOP**(或**LIMIT**在MySQL中)用于限制返回的记录数量,这在大数据集中快速获取部分结果时非常有用。 ### 12. SQL查询性能优化的一般原则 - 尽量避免全表扫描,通过建立合适的索引来加速查询。 - 减少...

    2009年最新版 win2003 IIS6+PHP5+MySQL5+Zend Optimizer+phpMyAdmin安装配置教程第1/8页

    本教程还提供了可下载的代码文件,包括HTML和UBB代码,方便用户进行查阅和转载。 #### 6. 专业技术提示 - **管理员权限**: 在配置过程中使用具有管理员权限的账号是必须的,因为IIS6和系统级别的软件安装、配置都...

    帝国cms个人博客(转载)

    【标题】"帝国cms个人博客(转载)"所涉及的知识点主要集中在网站开发领域,特别是使用帝国CMS系统来创建一个个人博客的过程。帝国CMS是一款基于PHP+MySQL开发的内容管理系统,它以其强大的功能、灵活的扩展性和易用...

    一米阳光项目转载phpwind668—关于PHP

    5. SEO 优化:支持自定义URL结构,提高搜索引擎可见性。 6. 插件与模板:丰富的插件库和可更换的模板,满足不同需求和个性化定制。 7. 安全防护:内置安全机制,防止SQL注入、XSS攻击等,保障网站安全。 【学习与...

    数据模型设计心得(转载)

    **工具**标签可能涉及数据建模工具,如MySQL Workbench、PowerDesigner等,这些工具可以帮助设计师可视化地创建和管理数据模型,生成数据库脚本,进行反向工程等。 广告数据模型设计可能涵盖以下几个方面: 1. **...

    Linux一键安装web环境全攻略

    任何未经过书面许可的复制、转载行为都将受到法律追究。 - **公司简介**: 上海驻云信息科技有限公司是一家专业的公有云架构技术及咨询服务提供商,专注于为企业提供全面的云解决方案和技术支持。公司具备强大的技术...

    H3_AP202106221499263958_1.pdf

    3. 数据存储与管理:报告中提供的数据必须存储在可靠的数据库管理系统(如MySQL, PostgreSQL, MongoDB等)中,并且需要进行高效的数据管理以保证数据的准确性和可访问性。 二、房地产行业研究与专业指导 1. 市场...

    A5站长网 全部织梦采集规则 仿站必备

    建议与原作者取得联系并获得授权后再行转载。 2. **内容质量**:虽然自动化采集可以节省大量人力物力成本,但也要注意筛选高质量的内容进行发布,避免低质甚至虚假信息对用户造成误导。 3. **SEO优化**:搜索引擎...

    thinkPHP5快速入门手册

    - **版权说明**:文档版权属于ThinkPHP官方,未经授权禁止任何形式的转载与下载。 - **版本优势**:更灵活的路由配置,依赖注入,请求缓存,更强大的查询语法,引入请求/响应对象,增强的模型功能等。 #### 一、...

    Learning Node.js Development

    - **数据库交互**:掌握如何使用MySQL、MongoDB等常见数据库,并实现CRUD(创建、读取、更新、删除)操作。 - **中间件使用**:了解Express框架中的中间件概念及其在处理请求过程中的作用,包括错误处理和日志记录。...

    phpLabware-开源

    下面将详细探讨phpLabware的功能、结构以及如何利用它来优化实验室工作流程。 ### 1. 试剂管理 在科研实验室中,试剂管理是一项关键任务。phpLabware提供了一个完善的试剂管理系统,支持对菌株、抗体、质粒、...

    二十三种设计模式【PDF版】

    体验创造快感的激动人心的过程. 为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解 GoF 的《设计模式》。由 于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用...

Global site tag (gtag.js) - Google Analytics