SQL优化案例一则
2016-05-14 22:58
453 查看
接前面一条<<SQL搞挂mysql问题处理案例>>一文(/article/11543534.html),现在需要对原SQL进行优化:
原SQL如下:
SELECT DISTINCT
o.CN,
o.WNE,
o.CON,
o.ON,
o.A_NAME,
o.A_NO,
o.CREATE_DATE receipt_time,
o.delivery_date delivery_time
FROM t o
WHERE o.CREATE_DATE BETWEEN '2016-05-02 20:39:23' AND '2016-05-12 17:00:23'
AND o.business_type = 'B2C'
AND o.is_delivery = 1
and o.order_type = 'PO'
AND o.WNe = 'xxxx小电仓'
AND o.A_CODE NOT IN (38549, 44926)
AND o.A_NO != ''
AND o.is_got = 0
AND o.IS_SIGN = 0
AND NOT EXISTS (
SELECT id FROM t_carrier
WHERE ON = o.ON
AND remark NOT IN (
'开始处理',
'待配货',
'开始处理',
'信息审核通过',
'已通知配货',
'待配货',
'您的包裹已打包'
)
)
ORDER BY o.WNE, o.CREATE_DATE limit 20
从processlist来查看,TIME为52113秒,SQL执行大约14小时没有完成,而手工kill,无法释线程资源,而堵住后面执行的多个类似SQL,应用人员将所有内外应用停了后,也没结束被kill的
线程。
show processlist\G
*************************** 3. row ***************************
Id: 38023534
User: lotsprd
Host: 172.16.12.155:44805
db: lots
Command: Killed
Time: 52113
State: Sending data
Info: SELECT DISTINCT
o.CN,
o.WNE,
o.CON,
o.ON
Progress: 0.000
原SQL执行计划如下:
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
|
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
| 1 | PRIMARY | o | range | index_A_no,index_createdate | index_createdate | 9 | NULL | 1043400 | Using index condition; Using where;
Using temporary |
| 2 | MATERIALIZED | t_carrier | ALL | index_ON | NULL | NULL | NULL | 11094365 | Using where
|
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
2 rows in set (0.25 sec)
查询表t_carrier记录数:
select count(*) from t_carrier;
+----------+
| count(*) |
+----------+
| 48582890 |
+----------+
1 row in set (5 min 17.92 sec)
t_carrier表定义:
show create table t_carrier\G
*************************** 1. row ***************************
Table: t_carrier
Create Table: CREATE TABLE `t_carrier` (
-------省略--------------------
PRIMARY KEY (`ID`),
KEY `index_A_NO` (`A_NO`),
KEY `index_ON` (`ON`),
KEY `index_accept_time` (`ACCEPT_TIME`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=51781874 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
查询表t表记录数:
select count(*) from t;
+----------+
| count(*) |
+----------+
| 6830407 |
+----------+
1 row in set (0.91 sec)
t表定义:
show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
--------省略---------
PRIMARY KEY (`ID`),
KEY `index_ON` (`ON`) USING BTREE,
KEY `index_cust_no` (`CON`) USING BTREE,
KEY `index_contract_no` (`contract_no`) USING BTREE,
KEY `index_A_no` (`A_NO`) USING BTREE,
KEY `index_createdate` (`CREATE_DATE`) USING BTREE,
KEY `index_extend1` (`extend1`) USING BTREE,
KEY `index_orderno_createdate` (`ON`,`CREATE_DATE`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7318664 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
查看CREATE_DATE和WNe字段条件返回的记录数:
lots 08:41:00>select count(*) from t where t.CREATE_DATE BETWEEN '2016-05-02 20:39:23' AND '2016-05-12 17:00:23';
+----------+
| count(*) |
+----------+
| 488760 |
+----------+
1 row in set (0.15 sec)
select count(*) from t where t.WNe = 'xxx小电仓';
+----------+
| count(*) |
+----------+
| 220462 |
+----------+
1 row in set (0.08 sec)
上面走字段WNe 上的索引访问 的数据更少些,两个组合在一起,就更好。
先增加索引:alter table lots.t add index (WNE,CREATE_DATE);
新执行计划:
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows |
Extra |
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
| 1 | PRIMARY | o | range | index_A_no,index_createdate,WNE | WNE | 612 | NULL | 34926 | Using index condition; Using
where; Using temporary |
| 2 | DEPENDENT SUBQUERY | t_carrier | index_subquery | index_ON | index_ON | 153 | func | 8 | Using where
|
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
查询返回内容省略,增加索引后的执行时间只需0.07秒。原来SQL执行了14小时都没完成。
新旧执行计划对比,主要是原来的执行计划中第一张访问的表都是全表扫描,访问 的rows行数特大,这个是主要问题,order表建了一个选择性好的索引后,执行计划也跟着改变了,在新执行计
划中,是用index_subquery方式访问 t_carrier表访问 的rows才8行,跟原执行计划中要访问11094365 行,真是天壤之别,index_subquery就是子查询是走非唯一索引扫描。
所以拿到SQL,第一要做的是看执行计划,执行计划第一要看的是第一次访问表是什么方式,一条原则,就是执行计划中的每步,访问的rows要少,少就性能好,一般是这样。
原SQL如下:
SELECT DISTINCT
o.CN,
o.WNE,
o.CON,
o.ON,
o.A_NAME,
o.A_NO,
o.CREATE_DATE receipt_time,
o.delivery_date delivery_time
FROM t o
WHERE o.CREATE_DATE BETWEEN '2016-05-02 20:39:23' AND '2016-05-12 17:00:23'
AND o.business_type = 'B2C'
AND o.is_delivery = 1
and o.order_type = 'PO'
AND o.WNe = 'xxxx小电仓'
AND o.A_CODE NOT IN (38549, 44926)
AND o.A_NO != ''
AND o.is_got = 0
AND o.IS_SIGN = 0
AND NOT EXISTS (
SELECT id FROM t_carrier
WHERE ON = o.ON
AND remark NOT IN (
'开始处理',
'待配货',
'开始处理',
'信息审核通过',
'已通知配货',
'待配货',
'您的包裹已打包'
)
)
ORDER BY o.WNE, o.CREATE_DATE limit 20
从processlist来查看,TIME为52113秒,SQL执行大约14小时没有完成,而手工kill,无法释线程资源,而堵住后面执行的多个类似SQL,应用人员将所有内外应用停了后,也没结束被kill的
线程。
show processlist\G
*************************** 3. row ***************************
Id: 38023534
User: lotsprd
Host: 172.16.12.155:44805
db: lots
Command: Killed
Time: 52113
State: Sending data
Info: SELECT DISTINCT
o.CN,
o.WNE,
o.CON,
o.ON
Progress: 0.000
原SQL执行计划如下:
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
|
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
| 1 | PRIMARY | o | range | index_A_no,index_createdate | index_createdate | 9 | NULL | 1043400 | Using index condition; Using where;
Using temporary |
| 2 | MATERIALIZED | t_carrier | ALL | index_ON | NULL | NULL | NULL | 11094365 | Using where
|
+------+--------------+--------------------+-------+-----------------------------------+------------------+---------+------+----------
+-----------------------------------------------------+
2 rows in set (0.25 sec)
查询表t_carrier记录数:
select count(*) from t_carrier;
+----------+
| count(*) |
+----------+
| 48582890 |
+----------+
1 row in set (5 min 17.92 sec)
t_carrier表定义:
show create table t_carrier\G
*************************** 1. row ***************************
Table: t_carrier
Create Table: CREATE TABLE `t_carrier` (
-------省略--------------------
PRIMARY KEY (`ID`),
KEY `index_A_NO` (`A_NO`),
KEY `index_ON` (`ON`),
KEY `index_accept_time` (`ACCEPT_TIME`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=51781874 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
查询表t表记录数:
select count(*) from t;
+----------+
| count(*) |
+----------+
| 6830407 |
+----------+
1 row in set (0.91 sec)
t表定义:
show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
--------省略---------
PRIMARY KEY (`ID`),
KEY `index_ON` (`ON`) USING BTREE,
KEY `index_cust_no` (`CON`) USING BTREE,
KEY `index_contract_no` (`contract_no`) USING BTREE,
KEY `index_A_no` (`A_NO`) USING BTREE,
KEY `index_createdate` (`CREATE_DATE`) USING BTREE,
KEY `index_extend1` (`extend1`) USING BTREE,
KEY `index_orderno_createdate` (`ON`,`CREATE_DATE`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7318664 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
查看CREATE_DATE和WNe字段条件返回的记录数:
lots 08:41:00>select count(*) from t where t.CREATE_DATE BETWEEN '2016-05-02 20:39:23' AND '2016-05-12 17:00:23';
+----------+
| count(*) |
+----------+
| 488760 |
+----------+
1 row in set (0.15 sec)
select count(*) from t where t.WNe = 'xxx小电仓';
+----------+
| count(*) |
+----------+
| 220462 |
+----------+
1 row in set (0.08 sec)
上面走字段WNe 上的索引访问 的数据更少些,两个组合在一起,就更好。
先增加索引:alter table lots.t add index (WNE,CREATE_DATE);
新执行计划:
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows |
Extra |
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
| 1 | PRIMARY | o | range | index_A_no,index_createdate,WNE | WNE | 612 | NULL | 34926 | Using index condition; Using
where; Using temporary |
| 2 | DEPENDENT SUBQUERY | t_carrier | index_subquery | index_ON | index_ON | 153 | func | 8 | Using where
|
+------+--------------------+--------------------+----------------+--------------------------------------------------+----------------------+---------+------+-------
+-----------------------------------------------------+
查询返回内容省略,增加索引后的执行时间只需0.07秒。原来SQL执行了14小时都没完成。
新旧执行计划对比,主要是原来的执行计划中第一张访问的表都是全表扫描,访问 的rows行数特大,这个是主要问题,order表建了一个选择性好的索引后,执行计划也跟着改变了,在新执行计
划中,是用index_subquery方式访问 t_carrier表访问 的rows才8行,跟原执行计划中要访问11094365 行,真是天壤之别,index_subquery就是子查询是走非唯一索引扫描。
所以拿到SQL,第一要做的是看执行计划,执行计划第一要看的是第一次访问表是什么方式,一条原则,就是执行计划中的每步,访问的rows要少,少就性能好,一般是这样。
相关文章推荐
- h2内存数据库
- 一条SQL搞挂mysql案例一则
- MySQL更新用户密码
- Oracle 是分区表,但条件不带分区条件的SQL
- MYSQL 3534错误
- Oracle 查找锁之间依赖关系的最源头SID
- [Linux]安装phpredis扩展
- Oracle 查询每天执行慢的SQL
- Oracle 查询锁之间的依赖关系
- Redis 字符串
- 数据库编程常见错误总结
- oracle常用函数汇总
- Oracle分析函数参考手册
- Oracle分析函数
- mysql启动正常但是访问时还是报错: ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061
- Mysql——锁问题
- PostgreSQL相关的软件,库,工具和资源集合
- SQL查询之联结查询和子查询
- MySQL存储过程+游标+触发器
- MySQL服务启动提示window无法启动MySQL服务 错误1067:进程意外终止