不使用带有 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 查询的主要内容,如果未能解决你的问题,请参考以下文章