mysql5.7 order by id desc limit n ;优化器选择主键索引

问题描述 投票:0回答:1
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 的估计行数的明确解释。

mysql indexing sql-order-by primary-key
1个回答
0
投票

问题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个测试用例无关。

© www.soinside.com 2019 - 2024. All rights reserved.