1. 程式人生 > >結合innodb的B+樹索引來優化sql查詢一例

結合innodb的B+樹索引來優化sql查詢一例

先上表結構:

CREATE TABLE `quote_xxxxx` (
  `instrument_id` varchar(20) NOT NULL,
  `time_type` varchar(20) NOT NULL,
  `datetime` datetime NOT NULL,
...
  `metal` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`instrument_id`,`time_type`,`datetime`),
  KEY `base` (`instrument_id`,`time_type`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

可以看到,表本身對(instrument_id,time_type,datetime)建立了聯合索引。 現在有這麼個查詢語句: select instrument_id,time_type,datetime,trading_day FROM quote_kline_1min where instrument_id ='xxxx' order by datetime DESC LIMIT 1 這張表的資料是每日遞增的,一開始沒問題,後來我們發現,慢查詢日誌裡面這句sql的查詢時間居然要達到幾秒之多。於是開始準備優化。

先explain了一下,發現是走的聯合索引,因為where instrument_id ='xxxx'

,那麼問題來了,怎麼會那麼慢呢,因為還有這個order by datetime DESC那麼這句sql的流程相當於是這樣的:先通過聯合索引把instrument_id ='xxxx'的行都取出來,這一步很快。然後對所有取出來的行進行臨時排序,現在慢就慢在這個臨時排序上面,因為where取出來的行特別多,又沒法通過datetime這個欄位來走索引排序,所以只能進行正常的排序。

於是,我們在datetime上面加了索引: ALTER TABLE quote_xxxx ADD INDEX datetime_index ( datetime ) 並且使用了FORCE INDEX,強制使用datetime這個索引。 發現並沒有太大的改善,現在流程變成了這樣

:先通過datetime索引把所有排序好了的行取出來,然後依次遍歷各個行,通過where條件匹配,匹配的回表拿資料,因為行特別多,這時候又沒法使用聯合索引,因為已經用了datetime索引。所以這樣看效率並不會有太大的提高。

推到重來,刪除datetime索引: ALTER TABLE quote_xxxx drop INDEX datetime_index 此時我們想到了再建立一個聯合索引,因為結合聯合索引在B+樹中的資料結構,比如說有個多列的聯合索引,就按多列資料排序,例如現在有(1,1) (2,2) (2,1) (1,2) 那在聯合索引中的葉子節點的資料順序就是(1,1)(1,2)(2,1)(2,2)這樣來進行排序的。

開始建立: ALTER TABLE quote_xxxx ADD INDEX instr_datetime_index ( instrument_id, datetime) 針對sql語句的instrument_iddatetime來建立聯合索引,再explain了一下: id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE quote_xxxx range PRIMARY,base,instr_datetime_index instr_datetime_index 82 NULL 84306 Using index condition

可以看到,優化器選擇了instr_datetime_index索引,速度也快了很多,效率大大提升, 現在流程變成了這樣:通過instr_datetime_index索引在已經經過排序了的葉子節點上把where條件匹配的行取出來,相當於where匹配和排序都利用到了這個索引,所以非常快。

至此,優化完成了,從原先的幾秒提升到了現在的幾十毫秒。