在 where 和 orderby 子句中使用两个单列索引

Posted

技术标签:

【中文标题】在 where 和 orderby 子句中使用两个单列索引【英文标题】:Using two single-column indexes in where and orderby clause 【发布时间】:2020-09-27 08:47:54 【问题描述】:

我用谷歌搜索了很多,但找不到明确的答案

假设我们有这个查询

SELECT * WHERE user_id = x ORDER BY date_created

如果我们在 user_id 上有一个单列索引,在 date_created 上有另一个索引,优化器会使用这两个索引吗?还是只是 user_id 索引?

【问题讨论】:

运行一个解释看看。 非常感谢......所以你说它不能同时使用这两个索引。对吗? mysql 将一次对特定表使用一个索引,您可以使用复合索引,即两者的组合(user_id, date_created) @RickJames 请删除 cmets,以及 your website 上关于 Firefox 的 FALSE 评论.... ???? @Luuk - 删除了虚假的 cmets;博客中过时的行已删除。感谢您指出。 【参考方案1】:

这是您的查询:

SELECT *
FROM mytable
WHERE user_id = 123 
ORDER BY date_created

如果您有两个不同的索引,那么 MySQL 可能会使用 user_id 上的索引来应用 where 谓词(如果它认为它会加快查询速度,具体取决于数据的基数和其他因素)。它不会使用date_created 上的索引,因为它无法将满足where 谓词的中间结果集与该索引相关联。

对于这个查询,您需要一个在(user_id, date_created) 上的复合索引。数据库使用索引中的第一个键来过滤数据集:在索引B树中,匹配的行已经按日期排序,因此order by操作变成了无操作。

我注意到您使用的是select *;一般来说,这不是一个好的做法,也不利于性能。如果表中除了用户和日期之外还有其他列,这将强制数据库查找表以通过索引过滤和排序后带来相应的行,这可能比根本不使用索引。如果您只需要几列,请列举它们:

SELECT date_created, first_name, last_name 
FROM mytable
WHERE user_id = 123 
ORDER BY date_created

并且在(user_id, date_created, first_name, last_name) 上有一个索引。这是一个覆盖索引:数据库可以在索引上执行整个查询,而无需查找表本身。

【讨论】:

。 .过滤查询时,文档的该部分可能不相关。它试图避免一种称为 thrashing 的情况,即表不适合内存并且被乱序读取。在这一点上,文档应该很清楚。而且,这与select * 无关,它与索引中的键以外的 any 列有关。天哪,文档的那部分确实具有误导性。 总的来说,GMB 的回答还是不错的。但是,正如戈登指出的那样,他走向了可能不相关的方向。一些说明:必须检查列,因此* 与列列表相比,工作量没有太大差异。 重要 是否存在“未记录”的不需要的TEXTBLOB 列。这可能需要额外的磁盘读取来执行 SELECT * 而不是避免该列。 和“非记录”提取可能导致“颠簸”。我将“好”索引视为处理WHEREGROUP BYORDER BY and LIMIT 的索引; “更好”的索引就是这样做并且“覆盖”的索引。但是,“覆盖”通常是不可能的(因为TEXT)或不实用(“太多”列)。 @GordonLinoff:我从我的回答中删除了该文档引用,因为它确实具有误导性(对我来说也是如此!)。 @RickJames:我打算在select * 讨论中介绍覆盖 概念。我稍微编辑了我的答案,以(希望)让它更清晰一些。

以上是关于在 where 和 orderby 子句中使用两个单列索引的主要内容,如果未能解决你的问题,请参考以下文章

iOS - Firestore 复合索引中的索引上的多个 orderBy 和 where 子句

我可以使用 where equals 子句和 orderby 查询 Cloud Firestore 集合吗?

数据库-过滤数据

oracle用WHERE替代ORDER BY

挽救数据库性能的30条黄金法则

挽救数据库性能的30条黄金法则