我有一个包含6列的索引。但MySQL永远不会使用整个索引。它只会使用前缀。
CREATE TABLE TestChannelData
(
hostId MEDIUMINT UNSIGNED NOT NULL,
sessionId MEDIUMINT UNSIGNED NOT NULL,
sessionFragment TINYINT UNSIGNED NOT NULL,
id INT UNSIGNED NOT NULL,
vcid INT UNSIGNED NULL,
dssId SMALLINT UNSIGNED NOT NULL
) ENGINE=InnoDB CHARSET=latin1;
CREATE TABLE TestChannelValue
(
hostId MEDIUMINT UNSIGNED NOT NULL,
sessionId MEDIUMINT UNSIGNED NOT NULL,
sessionFragment TINYINT UNSIGNED NOT NULL,
id INT UNSIGNED NOT NULL,
ertCoarse INT UNSIGNED NOT NULL,
ertFine INT UNSIGNED NOT NULL
) ENGINE=MyISAM CHARSET=latin1;
ALTER TABLE TestChannelValue
ADD KEY reverseIndex(hostId, sessionId, sessionFragment, id),
ADD KEY ertReverseIndex(ertCoarse, ertFine, hostId, sessionId, sessionFragment, id);
ALTER TABLE TestChannelValue
ADD KEY reverseErtIndex(hostId, sessionId, sessionFragment, id, ertCoarse, ertFine);
查询:
SELECT cv.sessionId, cv.hostId, cv.sessionFragment,
cd.dssId, cd.vcid,
cv.ertCoarse, cv.ertFine
FROM TestChannelData AS cd
STRAIGHT_JOIN TestChannelValue AS cv USE INDEX (ertReverseIndex)
ON ((cv.sessionId = cd.sessionId) AND
(cv.hostId = cd.hostId) AND
(cv.sessionFragment = cd.sessionFragment) AND
(cv.id = cd.id))
WHERE
(
(cv.ertCoarse > 0) AND
((cv.ertCoarse < 2000) OR ((cv.ertCoarse = 2000) AND (cv.ertFine = 0)))
)
解释扩展显示使用了indes,但key_len只是8而不是所需的19.如果我强制索引而不是reverseErtIndex,则key_ln为11。
如果我将ert列的比较替换为相等,则key_len为19。
为什么MySQL不会使用整个索引?
答案 0 :(得分:0)
11的key_len(对于reverseErtIndex)对应于JOIN谓词中的列(3 + 3 + 1 + 4)。只需要那些列来查找匹配的行。可能,EXPLAIN输出显示MySQL正在使用 ref
加入操作(显示在type
列中)。
修改查询时,WHERE
子句中的谓词都是相等的条件,那么 ref
连接操作可以扩展到其他列,所以你在EXPLAIN输出中得到一个更长的key_len。
对于索引列的不等式比较,MySQL不使用 ref
操作,但可以使用 range
操作。可能,MySQL更倾向于使用较短的密钥前缀上的 ref
操作而不是较长的密钥前缀上的 range
操作。
如果MySQL使用ertReverseIndex
索引,我们希望基于range
子句中的谓词,WHERE
操作的key_len为8。 (如果要删除ertFine
列上的谓词,我们希望在EXPLAIN输出中将key_len报告为4。)
MySQL是否引用(即“使用”)索引中的任何其他列以满足查询,这将通过“ Using index
的存在/不存在”反映出来在EXPLAIN输出的Extra列中。如果存在,那么我们知道它是查询的覆盖索引,并且MySQL没有引用基础表中的块。
最重要的是,你有一个平等和不等式谓词的组合。 MySQL可以使用 ref
(或 equal_ref
)操作来实现相等条件,但不能使用不等式条件。对于不等式,MySQL可以使用 range
操作。