您的位置:首页 > 数据库

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要少,少就性能好,一般是这样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: