結合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_id
和datetime
來建立聯合索引,再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匹配和排序都利用到了這個索引,所以非常快。
至此,優化完成了,從原先的幾秒提升到了現在的幾十毫秒。