这是我的表,具有约10,000,000行数据
CREATE TABLE `votes` (
`subject_name` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`subject_id` int(11) NOT NULL,
`voter_id` int(11) NOT NULL,
`rate` int(11) NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`subject_name`,`subject_id`,`voter_id`),
KEY `IDX_518B7ACFEBB4B8AD` (`voter_id`),
KEY `subject_timestamp` (`subject_name`,`subject_id`,`updated_at`),
KEY `voter_timestamp` (`voter_id`,`updated_at`),
CONSTRAINT `FK_518B7ACFEBB4B8AD` FOREIGN KEY (`voter_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
这是指数基数
因此,当我执行此查询时:
SELECT SQL_NO_CACHE * FROM votes WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
我期望它使用索引,voter_timestamp
但是mysql选择使用它来代替:
explain select SQL_NO_CACHE * from votes where subject_name = 'medium' and voter_id = 1001 and rate = 1 order by updated_at desc limit 20 offset 100;`
type:
index_merge
possible_keys:
PRIMARY,IDX_518B7ACFEBB4B8AD,subject_timestamp,voter_timestamp
key:
IDX_518B7ACFEBB4B8AD,PRIMARY
key_len:
102,98
ref:
NULL
rows:
9255
filtered:
10.00
Extra:
Using intersect(IDX_518B7ACFEBB4B8AD,PRIMARY); Using where; Using filesort
而且我得到了200-400ms的查询时间。
如果我强迫它使用正确的索引,例如:
SELECT SQL_NO_CACHE * FROM votes USE INDEX (voter_timestamp) WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
MySQL可以在1-2ms内返回结果
这是解释:
type:
ref
possible_keys:
voter_timestamp
key:
voter_timestamp
key_len:
4
ref:
const
rows:
18714
filtered:
1.00
Extra:
Using where
那么mysql为什么不voter_timestamp
为我的原始查询选择索引呢?
我曾试图为analyze table votes
,optimize table votes
,丢弃索引,然后重新添加,但MySQL的仍然使用了错误的指标。不太明白是什么问题。
(voter_id, updated_at)
。另一个索引将是(voter_id, subject_name, updated_at)
或(subject_name, voter_id, updated_at)
(不包含费率)。
subject_name='medium' and rate=1
)
LIMIT
,ORDER BY
否则MySQL不会进入,甚至无法进入。也就是说,如果没有完整的4列,它将收集所有相关行,对所有行进行排序,然后选择LIMIT
。 随着 4列索引,查询可以看完避免排序和停止仅该LIMIT
行。
subject_name = "medium"
部分,也可以选择合适的指数,没有必要指标rate