先上表结构:

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_xxxx 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索引在叶子节点上通过先导列instrument_id把where条件匹配的行取出来,此时这些行是经过datetime排好序了的,取出第一个就可以了,所以非常快。

至此,优化完成了,从原先的几秒提升到了现在的几十毫秒。