AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 332029
Accepted
Bear Bile Farming is Torture
Bear Bile Farming is Torture
Asked: 2023-10-11 13:47:00 +0800 CST2023-10-11 13:47:00 +0800 CST 2023-10-11 13:47:00 +0800 CST

前缀为连续/高基数的复合索引的有效性

  • 772

架构:

{
  time_utc: "milliseconds",
  city: "string",
  age: "integer"
}

指数:

{
  time_utc: 1,
  city: 1,
  age: 1
}

示例查询:

col.aggregate([
  {
    $match: { time_utc: { $exists: true }  city: "new york", age: { $gt: 18 } }
  },
  {
    $sort: { time_utc: -1 }
  }
])

我确信在这个查询中将使用复合索引,这样排序就会高效。

然而,$match文档/过滤阶段似乎效率不高,因为我们可以假设它time_utc在整个集合中是唯一的。

假设集合中有 100 万个文档,只返回 10 个。我们进一步假设这 10 个文档位于排序的底部time_utc。

在这种情况下,查询必须扫描索引的整个 100 万个值才能发现 10 个文档。这相当于完​​整的集合扫描。

让我们假设相反, 中只有 2 个唯一值time_utc。在这种情况下,看起来city和age字段的索引实际上是有效的。

我的推理正确吗?

index
  • 2 2 个回答
  • 27 Views

2 个回答

  • Voted
  1. Best Answer
    user20042973
    2023-10-17T00:35:22+08:002023-10-17T00:35:22+08:00

    我想说你的一些推理是正确的,思考索引结构的后果确实非常重要。像往常一样,@Wernfried Domscheit在他的回答中提出了一些要点。但我认为这里还有更多内容需要解开,所以让我们深入探讨。我们将首先直接回应一些陈述。

    我确信在这个查询中将使用复合索引,这样排序就会高效。

    索引可以用,是的。但是,正如我之前提到的,这并不意味着它将被使用。在存在其他可行的索引的情况下尤其如此,我们将在稍后讨论。

    ...我们可以假设它time_utc在整个系列中是独一无二的。

    假设集合中有 100 万个文档,只返回 10 个。我们进一步假设这 10 个文档位于排序的底部time_utc。

    在这种情况下,查询必须扫描索引的整个 100 万个值才能发现 10 个文档。

    正确的。不过,您所描述的行为/问题并不与扫描索引时结果所在的位置隔离。

    在您所描述的场景中,所需的索引扫描非常“宽”。这使得查询服务的效率相当低。不过,结果在该索引中的放置通常与查询的行为和性能无关。如果这 10 个文档也是索引扫描中的前 10 个文档,那么您的查询将花费相同的时间并执行相同的工作量。

    我们可以看到,通过在两个方向上发出排序时查看计划的执行统计信息,因为可以沿或方向explain扫描索引。使用问题中提供的降序排序:forwardreverse

    > db.foo.aggregate([ { $match: { time_utc: { $exists: true }, city: "new york", age: { $gt: 18 } } }, { $sort: { time_utc: -1 } }]).explain("executionStats").executionStats
    {
      nReturned: 10,
      totalKeysExamined: 1000000,
      totalDocsExamined: 10,
      ...
          keyPattern: { time_utc: 1, city: 1, age: 1 },
          direction: 'backward',
    

    并使用相反的(升序):

    > db.foo.aggregate([ { $match: { time_utc: { $exists: true }, city: "new york", age: { $gt: 18 } } }, { $sort: { time_utc: 1 } }]).explain("executionStats").executionStats
    {
      nReturned: 10,
      totalKeysExamined: 1000000,
      totalDocsExamined: 10,
      ...
          keyPattern: { time_utc: 1, city: 1, age: 1 },
          direction: 'forward',
    

    让我们假设相反, 中只有 2 个唯一值time_utc。在这种情况下,看起来city和age字段的索引实际上是有效的。

    是的。正如您在这里所暗示的,这是数据分布的结果,而不是这种情况下数据库的某些不同行为。

    使用上面的术语,索引扫描的边界仍然像以前一样“宽”。这里的区别在于索引的“宽度”要小得多。在这种有 2 个唯一值的情况下,我希望输出explain(可能)仅报告一些值seeks,这将反映索引在这方面“更窄”的逻辑结构。

    让我们进一步探讨一下。

    “宽”扫描

    一般来说,当索引定义以排序字段开头时,对该字段的索引扫描将不受限制。在您的情况下,您确实在同一字段 ( time_utc) 上有一个谓词条件,但它实际上并没有导致缩小扫描范围。我们可以在输出中看到explain:

    > db.foo.aggregate([ { $match: { time_utc: { $exists: true }, city: "new york", age: { $gt: 18 } } }, { $sort: { time_utc: -1 } }]).explain().queryPlanner.winningPlan.inputStage.indexBounds
    {
      time_utc: [ '[MaxKey, MinKey]' ],
      city: [ '["new york", "new york"]' ],
      age: [ '[inf.0, 18)' ]
    }
    

    无论集合中的数据如何,这些边界都是相同的。

    现在,如果我们查看IXSCAN原始数据集(100 万个唯一值)的执行统计信息,我们可以看到数据库需要在索引中跳转以查找相关部分,并最终查看整个数据:

    > db.foo.aggregate([{$group:{_id:'$time_utc'}},{$count:'uniqueTimes'}])
    [ { uniqueTimes: 1000000 } ]
    > db.foo.aggregate([ { $match: { time_utc: { $exists: true }, city: "new york", age: { $gt: 18 } } }, { $sort: { time_utc: -1 } }]).explain("executionStats").executionStats.executionStages.inputStage
    {
      keyPattern: { time_utc: 1, city: 1, age: 1 },
      keysExamined: 1000000,
      seeks: 999991,
      ...
    

    但是,当索引定义的主键只有 2 个不同的值时,数据库会跳过其中的大部分:

    > db.foo.aggregate([{$group:{_id:'$time_utc'}},{$count:'uniqueTimes'}])
    [ { uniqueTimes: 2 } ]
    > db.foo.aggregate([ { $match: { time_utc: { $exists: true }, city: "new york", age: { $gt: 18 } } }, { $sort: { time_utc: -1 } }]).explain("executionStats").executionStats.executionStages.inputStage
    {
      keyPattern: { time_utc: 1, city: 1, age: 1 },
      keysExamined: 12,
      seeks: 3,
      ...
    

    建议(ESR 指南)

    正如另一个答案提到的,如果结果集很小,那么排序的成本并不是特别高。

    但总的来说,MongoDB 建议索引的起点是他们所说的ESR 规则。使用这种方法,谓词 oncity是一个相等条件,因此应放在索引的第一位。time_utc然而,谓词是一个范围条件,因为它基本上意味着“除了缺失之外的任何值”。由于该字段在两个方面都使用(排序和范围),因此指南建议您尝试的索引是:

    {
      city: 1,
      time_utc: 1,
      age: 1
    }
    

    现在回到关于结果集大小的问题,通过交换第二个和第三个键并引发阻塞排序,您实际上确实有可能看到更好的性能:

    {
      city: 1,
      age: 1,
      time_utc: 1
    }
    

    这完全取决于数据选择性,并且某些谓词值可能比另一个索引更适合一个索引。您必须在自己的环境中进行测试和评估。

    • 1
  2. Wernfried Domscheit
    2023-10-11T17:15:38+08:002023-10-11T17:15:38+08:00

    首先,日期/时间值应该更好地正确存储为Date对象,而不是数字,甚至更糟糕的是字符串。Date对象始终是 UTC 时间,因此它应该满足您的要求。

    city如果and上的条件age仅返回 10 个文档,则不需要 上的索引time_utc。对于 10 个文档的排序,是否使用索引并不重要,无论是否time_utc有 1000 万个不同值或只有两个不同值。

    • 0

相关问题

  • 如何根据一行的字段在索引上创建多个条目?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 我在索引上放了多少“填充”?

  • RDBMS 上的“索引”是什么意思?[关闭]

  • 如何在 MySQL 中创建条件索引?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve