不使用带有 order 子句和更大限制 MariaDB 的索引的简单 SQL 查询

Posted

技术标签:

【中文标题】不使用带有 order 子句和更大限制 MariaDB 的索引的简单 SQL 查询【英文标题】:Simple SQL query not using index with order clause and larger limit MariaDB 【发布时间】:2021-07-15 09:10:42 【问题描述】:

我刚刚遇到数据库在选择大量数据时没有考虑索引的问题。

这是一个带有顺序子句的简单选择。

此查询(或任何其他限制少于一百万的查询)

SELECT * 
FROM mon_stat_detail 
WHERE 1 
ORDER BY id DESC
LIMIT 500000

正确使用列 id 上的索引(顺便说一句。它不是主索引而是唯一索引)

当这个查询时

SELECT *
FROM mon_stat_detail 
WHERE 1 
ORDER BY id DESC
LIMIT 1000000

正在使用文件排序。

表很大,大约有 6000 万。记录。

使用文件排序需要 15 分钟,并且由于文件排序而在磁盘上创建超过 20GB 的数据

但是,如果我在同一个查询上强制索引

SELECT * 
FROM mon_stat_detail FORCE INDEX (id_2) 
WHERE 1 
ORDER BY id DESC
LIMIT 1000000

它正在使用它,只需要几秒钟的时间。

知道为什么会这样吗?为什么我需要对这样一个简单的查询强制使用这个索引?

(精简架构:)

CREATE TABLE mon_stat_detail (
    id int(16) unsigned NOT NULL AUTO_INCREMENT, 
    sensor_id int(10) unsigned NOT NULL, 
    time datetime NOT NULL, 
    … other about 10 columns ... 
    PRIMARY KEY (sensor_id,time), 
    UNIQUE KEY id_2 (id), 
    … some more indexes and FK …
) ENGINE=InnoDB AUTO_INCREMENT=550579790 DEFAULT CHARSET=utf8

数据库:

服务器版本:10.1.48-MariaDB-0+deb9u2 - Debian 9.13 协议版本:10

【问题讨论】:

优化器设置中存在一定百分比的选定行会影响。如果选定行的百分比高于此值,则服务器假定表扫描比索引扫描更有效。但我不建议调整影响所有查询的优化器设置 - 强制使用索引是更安全的解决方案。 hm.. 在我们的例子中,它需要设置为 1.5%,这太低了。你知道具体是哪个选项吗? 不,我从不调整默认设置(不需要),并且。当然,我不记得了。例如,请参阅The Optimizer Cost Model。 没有设置(据我所知)来控制该截止值。此外,MariaDB 10.1 可能没有使用 Akina 链接中提到的“成本模型”。当 mysql 实现“成本模型”代码时,MariaDB 正在将优化器带向其他方向。尽管如此,这两种产品还是会在表扫描与索引 BTree 和数据 BTree 之间的弹跳之间进行选择。 要求一百万行是相当罕见的;客户将如何处理如此大量的数据? 【参考方案1】:

如果目标是获取 任何 百万行,请忽略 ORDER BY

如果目标是获取最后百万行并且id中没有间隙,则使用

  WHERE id > (SELECT MAX(id) FROM mon_stat_detail) - 1000000
  ORDER BY id  ASC    -- note; and no LIMIT is needed

如果可能存在差距,您可以查看运行此(在 Select 之前)是否有帮助:

  ANALYZE TABLE mon_stat_detail;

对于 InnoDB,它速度很快,它会刷新查询计划所基于的“统计信息”。

【讨论】:

谢谢瑞克。这可以防止文件排序。我仍然想知道为什么会这样。但如果没有人回答,我会接受你的回答。谢谢你。 @Stan - 请提供SHOW CREATE TABLE。听起来id 不是PRIMARY KEY,这将有助于回答您的问题。此外,它可能会导致一种更可靠的方式来实现您的目标。 我在问题描述中提到了这一点。 Id 上有唯一的索引。主键是 sensor_id+date,因为这是处理消息的方式。仍然如果 id 具有唯一键并且它在 ORDER BY .. 中使用而没有任何 WHERE 条件。我看不出它不用于带有 LIMIT 的选择的原因。顺便提一句。 CREATE TABLE 语句对于这里的限制来说太长了。并没有真正找到如何向您发送 PM 的方法。 创建表的缩减版 CREATE TABLE mon_stat_detail (id int(16) unsigned NOT NULL AUTO_INCREMENT, sensor_id int(10) unsigned NOT NULL, time datetime NOT NULL, ... 其他大约 10 列 ... PRIMARY KEY (sensor_id,time), UNIQUE KEY id_2 (id), ... 更多索引和 FK ...) ENGINE=InnoDB AUTO_INCREMENT=550579790 DEFAULT CHARSET=utf8 @Stan - 感谢“减少创建”;足够了。我被难住了。

以上是关于不使用带有 order 子句和更大限制 MariaDB 的索引的简单 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

带有 order by 子句的 SQL 查询

带有 UNION 子句的 ORDER BY

具有多列的Order by子句的语法不正确

选择行的间隔,包括一些带有 where 和 order 子句的行

《网站数据分析》8.8 使用TOP子句限制结果集

带有 WHERE 子句和 INNER JOIN 的 MySQL 更新查询不起作用