mysql利用hint的一次调优
2008-04-11 17:43
417 查看
昨天,开发人员报告,说测试机上一个sql突然变得很慢,sql如下:
select order0_.id as x0_0_
from order_tab order0_,
order_record_tab orderrecor1_,
order_factory_tab orderfacto2_,
relate_order_tab relateorde3_
where (order0_.id=orderrecor1_.order_id )
and(orderrecor1_.id=orderfacto2_.order_record_id )
and(relateorde3_.order_id=order0_.id )
and(orderfacto2_.id in(174028 , 174029))
and((relateorde3_.relate_internet_order_id is not null ))
and(relateorde3_.relate_internet_order_id not in
(select order4_.internet_order_id
from order_tab order4_,
order_record_tab orderrecor5_,
order_factory_tab orderfacto6_,
relate_order_tab relateorde7_
where (order4_.id=orderrecor5_.order_id )
and(orderrecor5_.id=orderfacto6_.order_record_id )
and(relateorde7_.order_id=order4_.id )
and(orderfacto6_.id in(174028 , 174029))
and((relateorde7_.relate_internet_order_id is not null )
)
)
);
此sql故障时的执行计划如下:
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | order4_ | ref_or_null |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| order_tab_internet_order_id | 5 |
func | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | ref |
PRIMARY,ORDER_ID
| ORDER_ID | 4 |
pointcard.order4_.ID | 1 | |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | ref |
PRIMARY,ORDER_RECORD_ID
| ORDER_RECORD_ID | 4 |
pointcard.orderrecor5_.ID | 1 | Using where |
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
很显然,这是一个用hibernate生成的sql语句,执行计划显示格式比较乱。
其中,在主查询和子查询中所涉及的4个表记录数如下:
mysql> select count(1) from order_tab order0_;
+----------+
| count(1) |
+----------+
| 30737230 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from order_record_tab orderrecor1_;
+----------+
| count(1) |
+----------+
| 31298527 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from order_factory_tab orderfacto2_;
+----------+
| count(1) |
+----------+
| 155895 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from relate_order_tab relateorde3_;
+----------+
| count(1) |
+----------+
| 9397 |
+----------+
1 row in set (0.00 sec)
按照sql的执行计划,主查询中先对orderfacto2_做范围扫描(range),然后和
orderrecor1_、order0_分别做等值连接(eq_ref),最后再和
relateorde3_做ref连接,这个过程是没有问题的。而子查询和主查询是完全系统的,但是子查询显然和主查询的执行计划不同,这点令人疑惑。在
生产机上此语句的执行计划如下:
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto6_.ORDER_RECORD_ID | 1 | |
| 2
| DEPENDENT SUBQUERY | order4_ | eq_ref |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| PRIMARY | 4 |
pointcard.orderrecor5_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
8 rows in set (0.03 sec)
和我们意料的一样,子查询的执行计划和主查询完全相同,速度也很快。怀疑测试机上的表数据统计信息是否没有更新,但是由于analyze时间很长,此步骤暂时不能做,想到的解决办法如下:
1、使用straight join按照主查询的连接查询进行连接;
2、让开发人员修改应用,将查询分为两步,先做子查询,得到结果后再做主查询;
3、和2想法类似,不过使用临时表存储子查询结果,再做主查询。
显然,方法1是解决问题最快的方法,我们改写后的sql如下:
select order0_.id as x0_0_
from order_tab order0_,
order_record_tab orderrecor1_,
order_factory_tab orderfacto2_,
relate_order_tab relateorde3_
where (order0_.id=orderrecor1_.order_id )
and(orderrecor1_.id=orderfacto2_.order_record_id )
and(relateorde3_.order_id=order0_.id )
and(orderfacto2_.id in(174028 , 174029))
and((relateorde3_.relate_internet_order_id is not null ))
and(relateorde3_.relate_internet_order_id not in
(select order4_.internet_order_id
from
order_factory_tab orderfacto6_
STRAIGHT_JOIN
order_record_tab orderrecor5_
STRAIGHT_JOIN
order_tab order4_
STRAIGHT_JOIN
relate_order_tab relateorde7_
where
(order4_.id=orderrecor5_.order_id
)and(orderrecor5_.id=orderfacto6_.order_record_id
)and(relateorde7_.order_id=order4_.id )and(orderfacto6_.id in(174028 ,
174029))and((relateorde7_.relate_internet_order_id is not null ))));
执行计划变为:
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto6_.ORDER_RECORD_ID | 1 | |
| 2
| DEPENDENT SUBQUERY | order4_ | eq_ref |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| PRIMARY | 4 |
pointcard.orderrecor5_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
8 rows in set (0.00 sec)
速度也随之变得飞快。
不过,最后应用还是按方法2进行了修改,让晦涩的语句变动更加简单、易读。
select order0_.id as x0_0_
from order_tab order0_,
order_record_tab orderrecor1_,
order_factory_tab orderfacto2_,
relate_order_tab relateorde3_
where (order0_.id=orderrecor1_.order_id )
and(orderrecor1_.id=orderfacto2_.order_record_id )
and(relateorde3_.order_id=order0_.id )
and(orderfacto2_.id in(174028 , 174029))
and((relateorde3_.relate_internet_order_id is not null ))
and(relateorde3_.relate_internet_order_id not in
(select order4_.internet_order_id
from order_tab order4_,
order_record_tab orderrecor5_,
order_factory_tab orderfacto6_,
relate_order_tab relateorde7_
where (order4_.id=orderrecor5_.order_id )
and(orderrecor5_.id=orderfacto6_.order_record_id )
and(relateorde7_.order_id=order4_.id )
and(orderfacto6_.id in(174028 , 174029))
and((relateorde7_.relate_internet_order_id is not null )
)
)
);
此sql故障时的执行计划如下:
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | order4_ | ref_or_null |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| order_tab_internet_order_id | 5 |
func | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | ref |
PRIMARY,ORDER_ID
| ORDER_ID | 4 |
pointcard.order4_.ID | 1 | |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | ref |
PRIMARY,ORDER_RECORD_ID
| ORDER_RECORD_ID | 4 |
pointcard.orderrecor5_.ID | 1 | Using where |
+----+--------------------+--------------+-------------+------------------------------------------------------------------------------+-----------------------------+---------+----------------------------------------+------+-------------+
很显然,这是一个用hibernate生成的sql语句,执行计划显示格式比较乱。
其中,在主查询和子查询中所涉及的4个表记录数如下:
mysql> select count(1) from order_tab order0_;
+----------+
| count(1) |
+----------+
| 30737230 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from order_record_tab orderrecor1_;
+----------+
| count(1) |
+----------+
| 31298527 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from order_factory_tab orderfacto2_;
+----------+
| count(1) |
+----------+
| 155895 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from relate_order_tab relateorde3_;
+----------+
| count(1) |
+----------+
| 9397 |
+----------+
1 row in set (0.00 sec)
按照sql的执行计划,主查询中先对orderfacto2_做范围扫描(range),然后和
orderrecor1_、order0_分别做等值连接(eq_ref),最后再和
relateorde3_做ref连接,这个过程是没有问题的。而子查询和主查询是完全系统的,但是子查询显然和主查询的执行计划不同,这点令人疑惑。在
生产机上此语句的执行计划如下:
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto6_.ORDER_RECORD_ID | 1 | |
| 2
| DEPENDENT SUBQUERY | order4_ | eq_ref |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| PRIMARY | 4 |
pointcard.orderrecor5_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
8 rows in set (0.03 sec)
和我们意料的一样,子查询的执行计划和主查询完全相同,速度也很快。怀疑测试机上的表数据统计信息是否没有更新,但是由于analyze时间很长,此步骤暂时不能做,想到的解决办法如下:
1、使用straight join按照主查询的连接查询进行连接;
2、让开发人员修改应用,将查询分为两步,先做子查询,得到结果后再做主查询;
3、和2想法类似,不过使用临时表存储子查询结果,再做主查询。
显然,方法1是解决问题最快的方法,我们改写后的sql如下:
select order0_.id as x0_0_
from order_tab order0_,
order_record_tab orderrecor1_,
order_factory_tab orderfacto2_,
relate_order_tab relateorde3_
where (order0_.id=orderrecor1_.order_id )
and(orderrecor1_.id=orderfacto2_.order_record_id )
and(relateorde3_.order_id=order0_.id )
and(orderfacto2_.id in(174028 , 174029))
and((relateorde3_.relate_internet_order_id is not null ))
and(relateorde3_.relate_internet_order_id not in
(select order4_.internet_order_id
from
order_factory_tab orderfacto6_
STRAIGHT_JOIN
order_record_tab orderrecor5_
STRAIGHT_JOIN
order_tab order4_
STRAIGHT_JOIN
relate_order_tab relateorde7_
where
(order4_.id=orderrecor5_.order_id
)and(orderrecor5_.id=orderfacto6_.order_record_id
)and(relateorde7_.order_id=order4_.id )and(orderfacto6_.id in(174028 ,
174029))and((relateorde7_.relate_internet_order_id is not null ))));
执行计划变为:
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
id | select_type | table | type |
possible_keys
| key | key_len |
ref | rows | Extra |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
|
1 | PRIMARY | orderfacto2_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 1
| PRIMARY | orderrecor1_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto2_.ORDER_RECORD_ID | 1 | |
| 1
| PRIMARY | order0_ | eq_ref |
PRIMARY
| PRIMARY | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using index |
| 1
| PRIMARY | relateorde3_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.orderrecor1_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | orderfacto6_ | range |
PRIMARY,ORDER_RECORD_ID
| PRIMARY | 4 |
NULL | 2 | Using where |
| 2
| DEPENDENT SUBQUERY | orderrecor5_ | eq_ref |
PRIMARY,ORDER_ID
| PRIMARY | 4 |
pointcard.orderfacto6_.ORDER_RECORD_ID | 1 | |
| 2
| DEPENDENT SUBQUERY | order4_ | eq_ref |
PRIMARY,idx_internet_order_id,order_tab_internet_order_id
| PRIMARY | 4 |
pointcard.orderrecor5_.ORDER_ID | 1 | Using where |
| 2
| DEPENDENT SUBQUERY | relateorde7_ | ref |
ORDER_ID,relate_order_tab_relate_internet_order_id,relate_order_tab_order_id
| relate_order_tab_order_id | 4 |
pointcard.order4_.ID | 1 | Using where |
+----+--------------------+--------------+--------+------------------------------------------------------------------------------+---------------------------+---------+----------------------------------------+------+-------------+
8 rows in set (0.00 sec)
速度也随之变得飞快。
不过,最后应用还是按方法2进行了修改,让晦涩的语句变动更加简单、易读。
相关文章推荐
- 记一次利用 idb文件和frm文件还mysql数据
- MYSQL一次调优经验
- 记一次mysql 调优
- 默认值才是王道:记一次MySQL服务器参数调优之旅
- mysql如何利用好左前缀索引调优
- 利用服务器的几个调优技巧,让 MySQL 服务器飞速运行
- 利用脚本实现mysql主库到备库数据同步(每五分钟同步一次增量)
- 一次SQL Server2005调优经历,利用报表产生分析结果
- 利用 Python 只连接一次 MySQL
- 记录一次mysql的调优心得
- 利用mysqldump导出导入mysql所有数据库
- 如何利用docker快速构建MySQL主从复制环境
- mysql 利用触发器(Trigger)让代码更简单
- 利用Java+MySQL实现附近功能实例
- MySQL 处理海量数据时的SQL语句调优
- MySQL架构优化实战系列1:数据类型与索引调优全解析
- 利用JDBC连接数据库(MySQL)
- EF Core下利用Mysql进行数据存储在并发访问下的数据同步问题
- javascript 24小时弹出一次的代码(利用cookies)
- Windows下利用phpmyadmin修改Mysql的root账号密码