43

考虑到性能改进,我想知道哪些索引对连接表有帮助(特别是在 Rails 3 has_and_belongs_to_many 上下文中使用)。

模型和表格设置

我的模型是Foo并且Bar根据 Rails 约定,我有一个名为bars_foos. 没有主键或时间戳使该表中的旧字段bar_id:integerfoo_id:integer. 我有兴趣知道以下哪些索引是最好的并且没有重复:

  1. 复合索引:add_index :bars_foos, [:bar_id, :foo_id]
    • 两个指标
    • 一个。add_index :bars_foos, :bar_id
    • B.add_index :bars_foos, :foo_id
  2. 1 和 2-B 的组合

基本上,我不确定复合索引是否足够,假设它是有帮助的开始。我相信复合索引可以用作第一项的单个索引,这就是为什么我确定使用所有三行肯定会导致不必要的重复。

可能的用法

最常见的用法将给出模型的实例Foo,我将bars使用 RoR 语法询问它的关联foo.bars,反之亦然,bar.foos用于模型的实例Bar

这些将分别生成 和 类型的查询,SELECT * FROM bars_foos WHERE foo_id = ?然后SELECT * FROM bars_foos WHERE bar_id = ?使用这些结果 ID 到SELECT * FROM bars WHERE ID in (?)SELECT * FROM foos WHERE ID in (?)

如果我不正确,请在评论中纠正我,但我不相信,在 Rails 应用程序的上下文中,它会尝试执行一个查询,它指定两个 ID,如SELECT * FROM bars_foos where bar_id = ? AND foo_id = ?.

数据库

如果有特定于数据库的优化技术,我很可能会使用 PostgreSQL。但是,其他使用此代码的人可能希望根据他们的 Rails 配置在 MySQL 或 SQLite 中使用它,因此感谢所有答案。

4

2 回答 2

35

答案

经常重复的答案,往往总是如此,“这取决于”。更具体地说,这取决于您的数据是什么以及如何使用它。

tl;dr 解释

我的具体案例(并涵盖所有未来的基础)的简短 tl;dr 答案是我怀疑的选择#2 。但是,选择 #3 可以正常工作,因为根据我对数据的使用情况,创建复合索引所使用的额外时间和空间可能会减少未来的查询查找。

完整的解释

这样做的原因是数据库试图变得聪明,并试图尽可能快地做事,而不管程序员的输入如何。添加索引时要考虑的最基本的项目是是否会通过此键查找此对象。如果是,索引可能有助于加快速度。然而,这个索引是否被使用都归结为选择性和字段的基数。

由于外键通常是另一个 AR 类的 ID,因此基数通常会很高。但同样,这取决于您的数据。在我的示例中,如果有很多Foos 但很少有Bars,我的连接表中的许多条目都会有 simliar bar_ids。由于bar_ids 具有低基数,bar_id因此可能永远不会使用 on 索引,并且可能会因为每次bars_foos创建新条目时让数据库花费时间和资源* 来添加到该索引而受到阻碍。许多Bars 和很少Foos 以及两者中的少数也是如此。

一般的教训是,在考虑表上的索引时,要确定条目是否都将由该字段查找,以及该字段是否具有高基数。也就是说,这个字段有很多不同的值吗?在大多数连接表的情况下,“这取决于”,我们必须更仔细地考虑数据代表什么以及关系本身。在我的情况下,我将同时拥有许多Foos 和s 并且将通过它们关联的 sBar来查找s ,反之亦然。Foobar

我在办公室得到的另一个好答案是,“你为什么要担心你的索引?构建你的应用程序!”

脚注

* 在有关 STI 指数的类似问题中,有人指出指数的成本非常低,因此如有疑问,只需添加即可。

于 2012-07-01T23:59:14.703 回答
6

取决于您将如何查询数据。

假设您要搜索所有这些...

  • WHERE bar_id = ?
  • WHERE foo_id = ?
  • WHERE bar_id = ? AND foo_id = ?

...那么您可能应该使用 index on{bar_id, foo_id}和 index on {foo_id}

虽然您也可以在 上创建第三个索引{bar_id},但维护额外索引的代价可能会超过在较小索引中更好地聚类的好处。


另外,你打算如何用索引覆盖你的查询?一些替代方案,例如...

  • {foo_id, bar_id}{bar_id}
  • {foo_id, bar_id}{bar_id, foo_id}

...可能会更好地涵盖某些类型的查询。

覆盖是一种平衡行为 - 有时仅出于覆盖目的将字段添加到索引是合理的,有时则不是。在您测量实际数据量之前,您不会知道。

(免责声明:我不熟悉 Ruby。这个答案纯粹是从数据库的角度来看的。)

于 2012-05-29T16:11:40.940 回答