复合索引的顺序

bil*_*oah 5 mysql optimization index-tuning

假设我有一个类似的查询:

SELECT *
FROM table_a
    JOIN table_b USING (id)
WHERE table_b.column = 1
Run Code Online (Sandbox Code Playgroud)

我有一个索引id和一个索引,column但我经常添加一个复合索引,两者都可以提高这样的查询效率。我的问题是关于索引中列的顺序。通过反复试验,我发现有时 DBMS 更喜欢连接索引,有时它更喜欢WHERE索引。

在上面的查询中,是否有一个硬性、快速的规则可以让我知道哪个键顺序最有效?

通常我只添加两个索引,运行EXPLAIN查询并检查哪个是首选的,然后删除另一个。但是这个过程感觉可以通过更好地理解确定索引顺序所涉及的逻辑来改进。

Ric*_*mes 5

对于这个查询

SELECT *
FROM table_a
    JOIN table_b USING (id)
WHERE table_b.column = 1
Run Code Online (Sandbox Code Playgroud)

最佳方式是执行它是

  1. WHERE子句提供了一些过滤,所以让我们利用它。也就是说,有一个以table_b 开头 的索引column。(稍后我们将讨论是否将其组合。)因此,优化器将使用该索引来查找table_b.
  2. 对于这些行中的每一行,JOINtable_a. (请注意,JOIN, notLEFT JOIN正在使用;LEFT JOIN是另一回事。)
  3. 要进入table_a,需要一个以 开头的索引id。(注: USING(id)是指table_a.id = table_b.id。)

到目前为止,我们有

b:  INDEX(column)
a:  INDEX(id)   -- though it probably exists as PRIMARY KEY(id)
Run Code Online (Sandbox Code Playgroud)

覆盖?

我们不知道这两个表中还有哪些其他列。如果列很少,那么构建“覆盖”索引可能很诱人。这是一个包含索引的所有所需的列的任何地方SELECT。好处是通过只查看索引的 BTree 而不必接触数据 BTree 来提高一些性能。

对于table_b,这很容易说INDEX(column, id)。如果只有那两列,那会很好(和“覆盖”)。但可能有更多的列。所以,可能INDEX(column)就是所有值得做的事情。

对于table_a,我假设它idPRIMARY KEY(根据定义,它是唯一的和索引)。所以那里不再需要什么了。

底线:使用上面列出的两个单列索引。

而这个例子并没有举例说明任何关于“复合”索引的内容。 有关更多信息,请参阅

基数和范围
基数和复合
单列索引
索引手册

但我经常添加一个复合索引,两者都可以提高这样的查询效率......

更好的例子

正如我所说,你的例子并不能说明这个问题。所以,我会尝试回答“我什么时候应该使用复合索引”?有很多情况(见链接);我给你一个简单的案例。

WHERE x = 1
  AND y > 2
Run Code Online (Sandbox Code Playgroud)

相关特征是:

  • x并且y在同一张桌子上。(不能跨两个表建立索引。)
  • AND用来。(OR无法优化。)
  • 其中一项测试是使用=. (如果两者都是范围,复合将无济于事。)
  • y是一个“范围”(例如: y>2, y LIKE 'm%', y BETWEEN ... AND ...)。

一般规则是

  1. 将所有=列放在首位(x在我的示例中)
  2. 一个范围列放在最后 ( y)

也就是说,您必须订购它INDEX(x,y)

对于WHERE x = 1 AND y = 2(两者=),您是否拥有或都没有关系。INDEX(x,y)INDEX(y,x)

另一个花絮:使用ENGINE=InnoDBPRIMARY KEY列隐式附加到每个辅助键上。因此,您INDEX(column)INDEX(column, id). 但这一事实在本次讨论中不起作用。

我意识到我不同意这里(和其他地方)的其他答案,但我坚持自己的立场。


Han*_*non 4

一个好的经验法则是使复合索引中的前导列尽可能具有选择性。想象这一点的一个好方法是用电话簿进行类比:假设您需要在电话簿中查找某人,并且有两个索引......第一个是姓氏,名字。第二个是名字、姓氏。您会使用哪个索引来查找名为 John Xylophone 的人?当然,您会使用 LastName、Firstname 索引,因为木琴条目非常少,并且比查找姓氏为木琴的所有 John 条目花费的时间要少得多。

因此,如果id具有高选择性,并且column具有低选择性,则您希望索引为(id, column),但是如果column具有高选择性,并且id具有低选择性,则您可能会受益于将索引定义为(column, id)

如果您在with(column, id)上连接两个表,则可能会看到正在使用索引,这会导致需要连接的行数大大减少。idwhere column = xx

  • @Kondybas - 抱歉,但我认为你所说的大部分内容要么是误导性的,要么是错误的。选择性与综合指数无关。排序规则(等)稍微慢一些;获取行是很大的成本。`%like%` 不好主要是因为前导通配符。表长度仅略有不同:示例——万亿行索引的深度仅为百万行索引的两倍。 (2认同)