DB索引用于同一组列的多个查询组合?

时间:2011-10-22 15:50:21

标签: mysql database performance indexing

我正在寻求有关如何考虑表所需的最少索引数量的指导,在该表中,您可以在同一列列上执行不同的查询组合。理想情况下,您的答案将从这个具体示例中抽象出一些经验法则(如果可能的话)。

这个项目符号列表代表了我桌面上常见的三种不同查询条件:

  • WHERE race_type =? AND recordable_type =? AND active =?
  • WHERE race_type =? AND recordable_id =? AND recordable_type =? AND active =?
  • WHERE user_id =? AND race_type =? AND recordable_id =? AND recordable_type =? AND active =?

注意:user_id(int),race_type(varchar),recordable_id(int),recordable_type(varchar),active(boolean)

我可以为每个索引创建单独的多列索引,但是你的DB性能专家可能会以不同的方式处理它。

如果我需要提供更多信息以获得最佳答案,请告诉我们。

6 个答案:

答案 0 :(得分:8)

如果您的条件是分层的(如您的示例中所示),则可以使用组合索引。 DBMS在同时处理多个索引时遇到问题。尽管有可能并且他们试图在这种情况下做到最好。

这不会改变您应该尝试为某个where子句设置特定索引的事实。如果更多的WHERE'索引可以组合成一个索引,然后释放一些空间和CPU周期。

让我们开始为每个WHERE指定一个索引:

index1 (race_type, recordable_type, active)
index2 (race_type, recordable_id, recordable_type, active)
index3 (user_id, race_type, recordable_id, recordable_type, active)

通常,您可以通过提升基数来优化订单。基数是列在数据集中的可能值的数量。在您的示例中,active是一个布尔值。 (请注意,boolean只能包含两个值这一事实并不重要。如果您知道它只有两个值,则可能是int :0和1 )。

active字段的低基数意味着只需一次查找,我们就可以消除一半可能的记录(当然,这取决于您的数据集)。在此步骤之后,您的第一个索引将如下所示:

index1 (active, race_type, recordable_type)

除了基数之外,您还应该注意字段之间的任何逻辑层次结构。在不确切知道这些名称的含义的情况下,我猜测某些种族类型将拥有自己的可记录。 - 这当然不会消除可录制多种种族使用的可能性,但你必须选择一个订单,这似乎更合乎逻辑。 - 因此我们将使用race_typerecordable_type订单。

现在让我们来看看第二个指数。您在此处介绍了recordable_id。在不知道您的数据集的情况下,我可以放心地假设recordable_id的基数将比recordable_type更高。换句话说,ID会比类型更多。我还怀疑类型和id之间的层次结构(闻起来像一对多)。所以让我们把它放在类似的类型之后:

index2 (active, race_type, recordable_type, recordable_id)

现在是时候关注另一个重要角度了。当修改您的数据库时,索引在您的硬盘上有自己的成本(实际上是免费的)和CPU周期。可以从左到右使用任何索引的子集。 index2基本上包含index1,因为它是index1 + recordable_id,所以你可以摆脱它并最终得到一个。

来到user_id。作为ID字段,它表示高基数(许多可能的值),但请注意,不是规则,"基数越高,后面的字段将是"。我们宁愿使用基数作为信标来帮助发现字段之间的层次结构关系。 (并缩小索引大小)。

user_id是否向个别参赛者指出我们正在查看的数据(多种可能性)?或者是上传数据的客户(极少数可能性)?这很难说。你可以将它附加到我们现有的index2,你最终会得到一个可以在所有三个场景中使用的索引:

search_index (active, race_type, recordable_type, recordable_id, user_id)

...或者它可能值得为此scanario提供第二个索引...

您的问题很特殊,因为您只在where子句中使用=。如果您有AND (race_type = 1 OR race_type=8)之类的内容,还有很多其他注意事项,更不用说><了。此外,如果您使用ORDER BY,可以将其考虑在您使用的索引中。

答案 1 :(得分:3)

第一步是对正在考虑优化的查询使用EXPLAIN。 MySQL explain将返回有关哪些索引将用于完成查询的重要信息,并将帮助您优化查询。

根据我的经验,我看到表格采用了任意数量的复合索引排列,它实际上取决于您的应用程序以及您将发出最多的查询。

您还应该考虑将varchar列更改为链接到查找表的ID。它会为您的数据库添加一些额外的架构,但您会获得以下好处:

  1. 如果您需要更改列的值,则只需更改一行,即数千行。

  2. 您考虑索引的所有列都是数字,本质上比varchars更快,并且在达到最大索引长度限制之前会产生更多开销。

答案 2 :(得分:2)

IMHO

alter table your_table
add index ( race_type, recordable_type, active, user_id, recordable_id);
// watch-out the max length allowed for an index

常见的列为race_type, recordable_type, active
我认为通过构建索引,所有5列都适合所有搜索模式。

如果提案无效,请告诉我

答案 3 :(得分:1)

在您的情况下,正确的索引是任何顺序的 user_id + race_type + recordable_id + recordable_type + active 。那很简单。你问过一般方法吗?在这里。

了解索引非常重要。 theem很复杂,所以我的回答很大。我建议阅读我的答案和问题,而不是docs

where,order和group by中使用的所有列都应具有索引。 Mysql使用二叉树进行索引。这意味着,索引可以从左到右部分地使用而没有间隙。例如。我们在(a,b)上有复合指数。所以:WHERE a = 1 AND b = 1 - 使用完整索引,WHERE a = 1 - 使用索引的一半 - 二叉树索引可以从左边部分使用,WHERE b = 1 - 使用fullscan(不能使用索引), WHERE (a = 0 OR a = 1) AND b = 1 - 使用fullscan(mysql不支持多个搜索分支)。

有些查询根本无法使用索引。例如。带有“OR”语句的查询(二进制树索引是后续的)。或col LIKE'%...%' - 二进制索引只能从左侧部分使用。

应用正确索引的Algorythm:获取您在“WHERE”中使用的所有唯一列名。从查询中出现的顺序获取订单和分组中的所有唯一列名称,并添加到“WHERE”中的字段(从右侧添加)。比缩小索引,所以它们仍然可以被mysql使用。

您的查询中没有任何订单,但订单也需要索引。所以我让你的例子更复杂一点:

  • WHERE race_type =? AND recordable_type =? AND active =? ORDER BY race_type
  • WHERE race_type =? AND recordable_id =? AND recordable_type =? AND active =? ORDER BY date DESC,
  • WHERE user_id =? AND race_type =? AND recordable_id =? AND recordable_type =? AND active =?按日期排序ASC

    1. 来自“WHERE”的索引:“race_type + recordable_type + active”,“race_type + recordble_id + recordable_type + active”和“user_id + race_type + recordable_id + recordable_type + active”。

    2. 从排序中添加索引:

      • race_type + recordable_type + active + race_type
      • race_type + recordble_id + recordable_type + active + date
      • user_id + race_type + recordable_id + recordable_type + active + date
    3. 缩小索引:

      • recordable_type + active + race_type(用于“WHERE”和“ORDER”)
      • recordable_type + active + race_type + recordble_id + date(转换两列,但在末尾留下“date”进行排序)
      • 没有变化(我们不能在“日期”之后移动“user_id”并尝试在此之前包含先前的索引)

请参阅索引#1包含在索引#2中,因此抛弃索引#1。最后我们有两个索引:

  • recordable_type +有效+ race_type + recordble_id +日期
  • user_id + race_type + recordable_id + recordable_type +有效+日期

不要忘记按更新和删除查询中使用的algorythm列进行索引。

答案 4 :(得分:1)

Mysql使用最左边的索引,这意味着,如果索引是复杂的(包含多于一列)查询在索引列列表中从左到右遍历索引,如果有void(查询的where或join语句没有将不再使用其他索引列)

快速提示,对于可以编写查询的可能值很少的字段,它会覆盖所有可能的值,这意味着仍然可以使用更多的索引列(例如,where(active = 0或active = 1)和...)

答案 5 :(得分:0)

您在WHERE条件中有以下字段:user_id,race_type,recordable_id,recordable_type和active。其中一些可能会重复指定的条件。

我按照以下方式订购了它们:

* WHERE race_type = ? AND recordable_type = ? AND active = ?
* WHERE race_type = ? AND recordable_type = ? AND active = ? AND recordable_id = ?
* WHERE race_type = ? AND recordable_type = ? AND active = ? AND recordable_id = ? AND user_id = ?

它允许我们创建一个复合索引:

ALTER TABLE table_name
  ADD INDEX IX_table_name (race_type, recordable_type, active, recordable_id, user_id);

如果表有其他索引或主键,请添加USE INDEX子句以使用命名索引:

SELECT * FROM table_name USE INDEX IX_table_name
WHERE
  race_type = ? AND recordable_type = ? AND active = ? AND recordable_id = ? AND user_id = ?