假设我有这样的查询:
SELECT *
FROM table_a
JOIN table_b USING (id)
WHERE table_b.column = 1
我有一个索引id
和一个索引,column
但我经常添加一个复合索引,两者都可以提高这样的查询效率。我的问题是关于索引中列的顺序。通过反复试验,我发现有时 DBMS 更喜欢连接索引,有时它更喜欢WHERE
索引。
在上面的查询中,我可以遵守一个硬性、快速的规则来知道哪个键顺序最有效?
通常我只是添加两个索引,EXPLAIN
在查询上运行并检查哪个是首选,然后删除另一个。但是这个过程感觉可以通过更好地理解确定索引顺序所涉及的逻辑来改进。
对于这个查询
最佳方式是执行它是
WHERE
子句提供了一些过滤,所以让我们利用它。也就是说,有一个以table_b
开头column
的索引。(稍后我们将讨论是否将其复合。)因此,优化器将使用该索引来查找table_b
.JOIN
到table_a
. (注意JOIN
, notLEFT JOIN
被使用;LEFT JOIN
是另一回事。)table_a
,需要一个以 开头的索引id
。(注:USING(id)
意思是table_a.id = table_b.id
。)到目前为止,我们有
覆盖?
我们不知道这两个表中还有哪些其他列。如果列很少,那么构建“覆盖”索引可能很诱人。这是一个索引,其中包含
SELECT
. 好处是通过仅查看索引的 BTree 而不必触及数据 BTree 来提高性能。因为
table_b
,这会很诱人INDEX(column, id)
。如果只有这两列,那会很好(并且“覆盖”)。但可能有更多的列。所以,这可能INDEX(column)
就是所有值得做的事情。对于
table_a
,我假设它id
是PRIMARY KEY
(根据定义,它是唯一的和索引)。所以那里不需要更多的东西。底线:使用上面列出的两个单列索引。
这个例子并没有举例说明“复合”索引。 有关更多信息,请参阅
基数和范围
基数和复合
单列索引
索引食谱
更好的例子
正如我所说,你的例子并没有说明这个问题。那么,我将尝试回答“何时应该使用复合索引”?有很多情况(见链接);我给你一个简单的案例。
相关特征是:
x
并且y
在同一张桌子上。(不能跨两个表建立索引。)AND
用来。(OR
无法优化。)=
. (如果两者都是范围,复合将无济于事。)y
是一个“范围”(例如:y>2
、、、y LIKE 'm%'
)y BETWEEN ... AND ...
。一般规则是:
=
列放在首位(x
在我的示例中)y
)也就是说,您必须订购它
INDEX(x,y)
。对于
WHERE x = 1 AND y = 2
(两者=
),您是否拥有或都没有关系。INDEX(x,y)
INDEX(y,x)
另一个花絮:使用
ENGINE=InnoDB
,PRIMARY KEY
列被隐式附加到每个辅助键上。因此,您INDEX(column)
的与 相同INDEX(column, id)
。但是这个事实在这个讨论中并没有发挥作用。我意识到我在这里(和其他地方)不同意其他答案,但我坚持自己的立场。
一个好的经验法则是使复合索引中的前导列尽可能具有选择性。一个很好的想象方法是用电话簿类比:想象你需要在电话簿中找到某人,并且有两个索引......第一个是姓氏,名字。第二个是名字,姓氏。您会使用哪个索引来查找名叫 John Xylophone 的人?当然,您会使用 LastName, Firstname 索引,因为 Xylophone 条目很少,而且与查看所有 John 条目以查找姓氏为 Xylophone 的条目相比,所花费的时间要少得多。
因此,如果
id
是高选择性且选择性column
低,您希望索引为(id, column)
,但是如果column
选择性高且选择性id
低,您可能会受益于将索引定义为(column, id)
。(column, id)
如果您将两个表连接id
在一起where column = x
,您可能会看到一个正在使用的索引,此时需要连接x
的行数大大减少。在您给出的示例中,如果您可以自由更改连接顺序,最好的选择是根本没有复合索引:
db<>在这里摆弄