之前可能已经提出过这个问题,但我没有找到一个涉及我特定问题的帖子。所以我有一个重度索引的表,其中包含超过2500万行,速度非常重要。我一直在努力尽可能地优化结果,但一些奇怪的事情不断发生。
我有这张桌子:
CREATE TABLE IF NOT EXISTS `externallinks_global` (
`url_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`paywall_id` INT UNSIGNED NOT NULL,
`url` VARCHAR(767) NOT NULL,
`archive_url` BLOB NULL,
`has_archive` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`live_state` TINYINT UNSIGNED NOT NULL DEFAULT '4',
`last_deadCheck` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
`archivable` TINYINT UNSIGNED NOT NULL DEFAULT '1',
`archived` TINYINT UNSIGNED NOT NULL DEFAULT '2',
`archive_failure` BLOB NULL DEFAULT NULL,
`access_time` TIMESTAMP NOT NULL,
`archive_time` TIMESTAMP NULL DEFAULT NULL,
`reviewed` TINYINT UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`url_id` ASC),
UNIQUE INDEX `url_UNIQUE` (`url` ASC),
INDEX `LIVE_STATE` (`live_state` ASC),
INDEX `LAST_DEADCHECK` (`last_deadCheck` ASC),
INDEX `PAYWALLID` (`paywall_id` ASC),
INDEX `REVIEWED` (`reviewed` ASC),
INDEX `HASARCHIVE` (`has_archive` ASC),
INDEX `ISARCHIVED` (`archived` ASC),
INDEX `APIINDEX1` (`has_archive` ASC, `live_state` ASC, `paywall_id` ASC),
INDEX `APIINDEX2` (`live_state` ASC, `paywall_id` ASC),
INDEX `APIINDEX3` (`live_state` ASC, `paywall_id` ASC, `archived` ASC),
INDEX `APIINDEX4` (`live_state` ASC, `archived` ASC),
INDEX `APIINDEX5` (`live_state` ASC, `paywall_id` ASC, `reviewed` ASC),
INDEX `APIINDEX6` (`live_state` ASC, `reviewed` ASC),
INDEX `APIINDEX7` (`has_archive` ASC, `live_state` ASC, `paywall_id` ASC, `archived` ASC, `reviewed` ASC),
INDEX `APIINDEX8` (`has_archive` ASC, `live_state` ASC, `archived` ASC, `reviewed` ASC));
当在WHERE子句中只使用一列进行查询时,它可以正常工作,但是,当我这样做时
EXPLAIN EXTENDED SELECT * FROM externallinks_global FORCE INDEX (APIINDEX1,APIINDEX2,APIINDEX3,APIINDEX4,APIINDEX5,APIINDEX6,APIINDEX7,APIINDEX8) LEFT JOIN externallinks_paywall ON externallinks_global.paywall_id=externallinks_paywall.paywall_id WHERE (
{live_state {1}} {paywall_status {1}}查看IN (0, 7) OR externallinks_global.paywall_id IN (SELECT paywall_id FROM externallinks_paywall WHERE
由于某种原因,它尝试使用REVIEWED索引而不是APIINDEX5。我试图强迫它使用它,但后来它决定不使用索引。我有点失落。我显然做错了什么,但我不知道是什么。
以下是上述查询中的EXPLAIN:
IN (3)) ) AND
答案 0 :(得分:1)
问题出在你的WHERE
条款中:
WHERE (live_state IN (0, 7) OR externallinks_global.paywall_id IN (SELECT paywall_id FROM externallinks_paywall WHEREpaywall_statusIN (3)) ) AND reviewed=0
您拥有的综合指数:
INDEX `APIINDEX5` (`live_state` ASC, `paywall_id` ASC, `reviewed` ASC)
可用于通过任何最左边的前缀(列的组合)进行查询:
如果表具有多列索引,则优化程序可以使用索引的任何最左前缀来查找行。
但您要做的是按live_state
或 paywall_id
查询,使其独立于live_state
并让Mysql无法使用索引问题
Mysql documentation说明了这一点:
假设某个表具有以下规范:
CREATE TABLE test (
id INT NOT NULL,
last_name CHAR(30) NOT NULL,
first_name CHAR(30) NOT NULL,
PRIMARY KEY (id),
INDEX name (last_name,first_name)
);
...
但是,在以下查询中,名称索引未使用用于查找:
SELECT * FROM test WHERE first_name='Michael';
SELECT * FROM test
WHERE last_name='Widenius' OR first_name='Michael';
您可以尝试做的就是将查询拆分为两部分,
与UNION
合并。
第一部分将通过以下方式查询:
live_state IN (0, 7) AND reviewed=0
(应使用INDEX APIINDEX6
)
,第二部分将通过以下方式查询:
paywall_id IN (...) AND reviewed=0
(使用INDEX PAYWALLID
除非您添加新的paywall_id +已审核索引)。