...
# Query_time: 59.503827 ?Lock_time: 0.000198 ?Rows_sent: 641227 ?Rows_examined: 13442472 ?Rows_affected: 0
...
select uid,sum(power) powerup from t1 where?
date>='2017-03-31' and?
UNIX_TIMESTAMP(STR_TO_DATE(concat(date,' ',hour),'%Y-%m-%d %H'))>=1490965200 and?
UNIX_TIMESTAMP(STR_TO_DATE(concat(date,' ',hour),'%Y-%m-%d %H'))<1492174801 ?and?
aType in (1,6,9) group by uid;
實話說,看到這個SQL我也忍不住想罵人啊,究竟是哪個腦殘的XX狗設計的?
竟然把日期時間中的 date 和 hour 給獨立出來成兩列,查詢時再合并成一個新的條件,簡直無力吐槽。
吐槽歸吐槽,該干活還得干活,誰讓咱是DBA呢,SQL優化是咱的拿手好戲不是嘛~
SQL優化之路 SQL優化思路 不厭其煩地再說一遍SQL優化思路。
想要優化一個SQL,一般來說就是先看執行計劃,觀察是否盡可能用到索引,
同時要關注預計掃描的行數,
以及是否產生了臨時表(Using temporary) 或者?
是否需要進行排序(Using filesort),
想辦法消除這些情況。
SQL性能瓶頸定位 毫無疑問,想要優化,先看表DDL以及執行計劃:
CREATE TABLE `t1` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`date` date NOT NULL DEFAULT '0000-00-00',`hour` char(2) NOT NULL DEFAULT '00',`kid` int(4) NOT NULL DEFAULT '0',`uid` int(11) NOT NULL DEFAULT '0',`aType` tinyint(2) NOT NULL DEFAULT '0',`src` tinyint(2) NOT NULL DEFAULT '1',`aid` int(11) NOT NULL DEFAULT '1',`acount` int(11) NOT NULL DEFAULT '1',`power` decimal(20,2) DEFAULT '0.00',PRIMARY KEY (`id`,`date`),UNIQUE KEY `did` (`date`,`hour`,`kid`,`uid`,`aType`,`src`,`aid`)
) ENGINE=InnoDB AUTO_INCREMENT=50486620 DEFAULT CHARSET=utf8mb4
/*!50500 PARTITION BY RANGE ?COLUMNS(`date`)
(PARTITION p20170316 VALUES LESS THAN ('2017-03-17') ENGINE = InnoDB,PARTITION p20170317 VALUES LESS THAN ('2017-03-18') ENGINE = InnoDB
...
yejr@imysql.com[myDB]> EXPLAIN select uid,sum(power) powerup from t1 where?
date>='2017-03-31' and?
UNIX_TIMESTAMP(STR_TO_DATE(concat(date,' ',hour),'%Y-%m-%d %H'))>=1490965200 and?
UNIX_TIMESTAMP(STR_TO_DATE(concat(date,' ',hour),'%Y-%m-%d %H'))<1492174801 ?and?
aType in (1,6,9) group by uid\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: p20170324,p20170325,....all partitiontype: ALL
possible_keys: didkey: NULLkey_len: NULLref: NULLrows: 25005577filtered: 15.00Extra: Using where; Using temporary; Using filesort
明顯的,這個SQL效率非常低,全表掃描、沒有索引、有臨時表、需要額外排序,什么倒霉催的全趕上了。
優化思考 這個SQL是想統計符合條件的power列總和,雖然 date 列已有索引,但WHERE子句中卻對 date 列加了函數,而且還是 date 和 hour 兩列的組合條件,那就無法用到這個索引了。
還好,有個聰明伶俐的妹子,突發起想(事實上這位妹子本來就擅長做SQL優化的~),可以用 CASE WHEN 方法來改造下SQL,改成像下面這樣的:
select uid,sum(powerup+powerup1) from
(select uid,case when concat(date,' ',hour) >='2017-03-24 13:00' then power else '0' end as powerup,case when concat(date,' ',hour) < '2017-03-25 13:00' then power else '0' end as powerup1from t1where date>='2017-03-24'?and ??date <'2017-03-25'and ?aType in (1,6,9)
) a ?group by uid;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: p20170324type: range
possible_keys: didkey: idx2_date_addRedTypekey_len: 4ref: NULLrows: 876375filtered: 30.00Extra: Using index condition; Using temporary; Using filesort
yejr@imysql.com[myDB]> ALTER TABLE t1 ADD INDEX idx_uid(uid);
yejr@imysql.com[myDB]> EXPLAIN select uid,sum(powerup+powerup1) from
(select uid,case when concat(date,' ',hour) >='2017-03-24 13:00' then power else '0' end as powerup,case when concat(date,' ',hour) < '2017-03-25 13:00' then power else '0' end as powerup1from t1where date>='2017-03-24'?and ??date <'2017-03-25'and ?aType in (1,6,9)
) a ?group by uid\G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: if_date_hour_army_countpartitions: p20170331,p20170401...type: index
possible_keys: did,idx_uidkey: idx_uidkey_len: 4ref: NULLrows: 12701520filtered: 15.00Extra: Using where