CREATE TABLE `test` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`month` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
`b` varchar(255) NOT NULL DEFAULT '',
`c` varchar(255) NOT NULL DEFAULT '',
`d` varchar(255) NOT NULL DEFAULT '',
`e` varchar(255) NOT NULL DEFAULT '',
...
PRIMARY KEY (`id`),
INDEX `idx_month_b`(`month`, `b`) USING BTREE,
INDEX `idx_d`(`c`) USING BTREE,
...
);
背景
表中总记录数约为400万条; 月份字段的数据分布不太均匀,一半以上的数据集中在2023年以后。 月份字段的索引基数为 80。'c' 字段的索引基数为 265 万+。
问题1
性能
select * from test where month>='2024-07-01' and month<='2024-07-31' and d=? order by id desc limit 10;
条件月>='2024-07-01'和月<='2024-07-31', there are actually 210,000 records. The actual execution result of the SQL is empty. The execution plan shows that the primary key index was used, with rows=85. When I explicitly specified to use the index (month, b), the rows=440,000. I expected it to use the (month, b) index, and the actual execution result is significantly faster when using the forced index.
为什么?
为什么没有使用预期的索引,为什么使用主键时预计扫描行数这么低?
问题2
性能
select * from test where c='test1' order by id desc limit 1;
select * from test where c='test2' order by id desc limit 1;
c 列索引具有良好的选择性,不同的 c 值,记录数量从 1 到 4,000 不等。 在条件c='test1'下,有3,000条记录,而在条件c='test2'下,有2,900条记录。 SQL实际执行结果不为空。 当c='test1'时,查询使用c上的索引,而c='test2'时,查询使用主键索引,导致性能差异显着。
为什么? 在数据量如此接近的情况下,存在哪些可能导致数据量较小的查询使用主键索引?研究表明,在单列索引下,主键可能不是完全有序的,对吗? 为什么没有使用预期的索引(c),为什么使用主键时预计扫描行数这么低?
提高限额后,将达到预期指标。 我发现了一个有趣的问题。在问题1中,当使用主键索引时,逐渐增加限制会导致行数相应增加。例如,limit=1,rows=8; limit=2,rows=16,依此类推,直到达到某个阈值。我怀疑这是因为主键的估计行数超过了索引的估计扫描行数,之后选择了索引。此外,我发现主键的估计扫描行数等于 limit 乘以(总行数/索引 C 的估计行数)。但是,我找不到关于如何计算索引 C 的估计行数的明确解释。
问题1:
select *
from test
where month>='2024-07-01'
and month<='2024-07-31'
and d=?
order by id desc
limit 10;
需要
INDEX(d, month, id)
-- 按此顺序
请注意,
month<='2024-07-31'
不会捕获七月最后一天的东西。 建议:
where month >= '2024-07-01'
and month < '2024-07-01' + INTERVAL 1 MONTH
and ...
c='test2',有 2,900 个,但使用
PRIMARY
-- 执行 ANALYZE TABLE test;
来查看它是否开始使用 INDEX(c)
(应该如此)。 ANALYZE
很少需要;这里听起来好像没有得到“正确”的统计数据。
此外,优化器在决定做什么时通常不会注意到
LIMIT
的存在。
MySQL 什么版本? 如果我的答案不充分,请提交错误报告:bugs.mysql.com .
month
的基数和以month
开头的索引似乎与您的3个测试用例无关。