如何正确索引 MySQL 中多对多连接的链接表?

Posted

技术标签:

【中文标题】如何正确索引 MySQL 中多对多连接的链接表?【英文标题】:How to properly index a linking table for many-to-many connection in MySQL? 【发布时间】:2010-10-08 22:48:04 【问题描述】:

假设我在表“table1”和“table2”之间有一个简单的多对多表,它由两个 int 字段组成:“table1-id”和“table2-id”。我应该如何索引这个链接表?

我以前只是创建一个复合主索引 (table1-id,table2-id),但我读到如果您更改查询中字段的顺序,该索引可能不起作用。那么最佳解决方案是什么 - 为没有主索引的每个字段创建独立索引?

谢谢。

【问题讨论】:

这里讨论了多对多的最佳索引:mysql.rjweb.org/doc.php/… 【参考方案1】:

这取决于您的搜索方式。

如果你这样搜索:

/* Given a value from table1, find all related values from table2 */
SELECT *
FROM table1 t1
JOIN table_table tt ON (tt.table_1 = t1.id)
JOIN table2 t2 ON (t2.id = tt.table_2)
WHERE t1.id = @id

那么你需要:

ALTER TABLE table_table ADD CONSTRAINT pk_table1_table2 (table_1, table_2)

在这种情况下,table1 将在 NESTED LOOPS 中领先,并且您的索引只有在 table1 被首先索引时才可用。

如果你这样搜索:

/* Given a value from table2, find all related values from table1 */
SELECT *
FROM table2 t2
JOIN table_table tt ON (tt.table_2 = t2.id)
JOIN table1 t1 ON (t1.id = tt.table_1)
WHERE t2.id = @id

那么你需要:

ALTER TABLE table_table ADD CONSTRAINT pk_table1_table2 (table_2, table_1)

出于上述原因。

这里不需要独立的索引。复合索引可以在可以使用第一列的普通索引的任何地方使用。如果您使用独立索引,您将无法有效地搜索这两个值:

/* Check if relationship exists between two given values */
SELECT 1
FROM table_table
WHERE table_1 = @id1
  AND table_2 = @id2

对于这样的查询,两列至少需要一个索引。

为第二个字段增加一个索引总是不错的:

ALTER TABLE table_table ADD CONSTRAINT pk_table1_table2 PRIMARY KEY (table_1, table_2)
CREATE INDEX ix_table2 ON table_table (table_2)

主键将用于搜索on both values 和基于table_1 值的搜索,附加索引将用于基于table_2 值的搜索。

【讨论】:

感谢您的详细回答,但如果我双向搜索会怎样?我也在使用 Hibernate,所以我什至不确定它使用的是哪种方式。 如果您同时搜索两种方式,您将需要两个索引:一个用于 PRIMARY KEY 的复合索引,一个用于 PRIMARY KEY 中第二个列的普通索引。它在我的帖子底部。 在最后的双索引示例中,将非 pk 索引作为反向的两列而不是单列的组合是否有任何优势?即CREATE INDEX ix_table2_table1 ON table_table (table_2, table_1) 而不是CREATE INDEX ix_table2 ON table_table (table_2) ? 为了回答我自己的问题,@Quassnoi 建议的单列索引似乎隐含了第二列。所以,这只是简洁与明确的问题。来源:mysql.rjweb.org/doc.php/…【参考方案2】:

@Quassnoi,在您的第一个查询中,您实际上只使用了tt.table_1 键,正如我们从 WHERE 子句中看到的那样:WHERE t1.id = @id。而在第二个查询中 - 只有tt.table_2

因此,由于WHERE table_1 = @id1 AND table_2 = @id2,多列索引可能仅在第三个查询中有用。如果不使用此类查询,您认为是否值得使用两个单独的单列索引?

【讨论】:

单独的一列索引实际上总是一种浪费——多列(“复合”)索引从一列开始就足够了。 经常,但并非总是如此。索引中有更多列会增加索引的大小和更新频率。因此,只有在有从中受益的查询时才应该向索引添加额外的列。更好的措辞是:删除一个索引,如果它构成另一个索引的前缀。【参考方案3】:

只要您在查询中指定了这两个键,它们在查询中的顺序无关紧要,您在索引中指定它们的顺序也无关紧要。

但是,您有时可能只有一个或另一个键。如果您有时只有 id_1,那应该是第一个(但您仍然只需要一个索引)。

如果您有时有一个,有时另一个,有时两者都有,您将需要一个包含两个键的索引,以及一个包含一个字段的第二个(非唯一)索引 - 这两个键的选择性更强 - 以及主复合索引应该从另一个键开始。

【讨论】:

我最喜欢你的回答,但没有专业知识来验证它。 “更具选择性”是什么意思? 一个更专业的词是“基数”。这意味着该字段有多少不同的值。在一个极端,高基数,每一个值都是独一无二的。另一方面,某些字段可能只有几个不同的值,在这种情况下,索引不会在磁盘读取方面节省太多。 只有当WHERE 子句的两列都用= constant 测试时,第1 段才是正确的。否则,索引中的列的顺序可能会产生很大的不同。 第 3 段部分不正确。 在复合(多列)索引中,各个列的“选择性”(又名“基数”)无关紧要。将复合键视为列的连接;各个列的选择性不再相关。

以上是关于如何正确索引 MySQL 中多对多连接的链接表?的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis中多对多关系的映射和查询

MYSQL在一个语句中多对多选择(聊天,最新消息,用户名)

防止 SQLAlchemy 中多对多关系中的重复表条目

数据库表中多对多关系怎么设计?

数据库中多对多关系及其实现

SQL Server:多对多连接的最佳索引策略