强制 MySQL 在 Join 上使用两个索引

Posted

技术标签:

【中文标题】强制 MySQL 在 Join 上使用两个索引【英文标题】:Force MySQL to use two indexes on a Join 【发布时间】:2011-06-17 23:35:02 【问题描述】:

我试图强制 mysql 使用两个索引。我正在加入一个表,我想利用两个索引之间的交叉。具体术语是 Using intersect,这里是 MySQL 文档的链接:

http://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html

有没有办法强制执行此操作?我的查询正在使用它(并且它加快了处理速度),但现在无论出于何种原因它都停止了。

这是我想要执行此操作的 JOIN。我希望查询使用的两个索引是 scs.CONSUMER_ID_1 和 scs_CONSUMER_ID_2

JOIN survey_customer_similarity AS scs
    ON cr.CONSUMER_ID=scs.CONSUMER_ID_2 
    AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_1 
    OR cr.CONSUMER_ID=scs.CONSUMER_ID_1 
    AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_2

【问题讨论】:

请发布 MySql 版本、表定义和 EXPLAIN 输出。 此外,您能否显示整个查询...或至少其余大部分可能不考虑“机密”...从 WhatTable 中选择 X...加入...在哪里...分组... 【参考方案1】:

请参阅 MySQL 文档以获取 FORCE INDEX

JOIN survey_customer_similarity AS scs 
FORCE INDEX (CONSUMER_ID_1,CONSUMER_ID_2)
ON
cr.CONSUMER_ID=scs.CONSUMER_ID_2 
AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_1 
OR cr.CONSUMER_ID=scs.CONSUMER_ID_1 
AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_2

正如 TheScrumMeister 在下面指出的,这取决于您的数据,是否可以同时使用两个索引。


这是一个示例,您需要强制表出现两次以控制查询执行和交集。

使用它来创建一个包含 >100K 记录的表,其中大约 1K 行与过滤器 i in (2,3) 匹配,1K 行与 j in (2,3) 匹配:

drop table if exists t1;
create table t1 (id int auto_increment primary key, i int, j int);
create index ix_t1_on_i on t1(i);
create index ix_t1_on_j on t1(j);
insert into t1 (i,j) values (2,2),(2,3),(4,5),(6,6),(2,6),(2,7),(3,2);
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i*2, j*2+i from t1;
insert into t1 (i,j) select i, j from t1;
insert into t1 (i,j) select i, j from t1;
insert into t1 (i,j) select 2, j from t1 where not j in (2,3) limit 1000;
insert into t1 (i,j) select i, 3 from t1 where not i in (2,3) limit 1000;

做的时候:

select t.* from t1 as t where t.i=2 and t.j=3 or t.i=3 and t.j=2

你会得到 8 个匹配项:

+-------+------+------+
| id    | i    | j    |
+-------+------+------+
|     7 |    3 |    2 |
| 28679 |    3 |    2 |
| 57351 |    3 |    2 |
| 86023 |    3 |    2 |
|     2 |    2 |    3 |
| 28674 |    2 |    3 |
| 57346 |    2 |    3 |
| 86018 |    2 |    3 |
+-------+------+------+

在上面的查询中使用EXPLAIN得到:

id | select_type | table | type  | possible_keys         | key        | key_len | ref  | rows | Extra
1  | SIMPLE      | t     | range | ix_t1_on_i,ix_t1_on_j | ix_t1_on_j | 5       | NULL | 1012 | Using where

即使我们在两个索引EXPLAIN 上的查询中添加FORCE INDEX 也会返回完全相同的东西

要让它跨两个索引收集,然后将它们相交,请使用:

select t.* from t1 as a force index(ix_t1_on_i)

join t1 as b force index(ix_t1_on_j) on a.id=b.id

where a.i=2 and b.j=3 or a.i=3 and b.j=2

将该查询与explain 一起使用以获取:

id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra
1  | SIMPLE      | a     | range | ix_t1_on_i    | ix_t1_on_i | 5       | NULL | 1019 | Using where
1  | SIMPLE      | b     | range | ix_t1_on_j    | ix_t1_on_j | 5       | NULL | 1012 | Using where; Using index

这证明索引正在被使用。但这可能会也可能不会更快,具体取决于许多其他因素。

【讨论】:

我相信如果原来的解释只使用其中的一个索引,FORCE INDEX 不会强迫它使用 both 我认为 force index 比 use index 更强,即如果它可以使用,它会。那么1+2不会对交集进行散列吗? force index 更强,它告诉查询优化器使用列表中的任何/所有索引。因此,如果最初的计划使用表扫描是因为它认为扫描更便宜,那么它会起作用。但是,如果 - 无论出于何种原因 - 优化器无法同时使用这两个索引,force index... 将无法解决问题。 @RichardTheKiwi,很奇怪,当我在上面运行您的插入时,我得到了 116k 行。你是怎么得到 400k 的? @Pacerier 感谢您指出这一点。我又添加了两个插入以将其提高到 460K。【参考方案2】:

MySQL 仅支持每个连接使用单个索引。如果您希望它在连接中使用两列作为索引,您应该在这两列上创建一个索引。请注意,这并不像看起来那么糟糕,因为 (a,b) 上的索引会兼作 a 上的索引。

见the MySQL manual

如果列不构成索引的最左前缀,则 MySQL 不能使用索引。

【讨论】:

由于index merge 优化而不正确。 这是一条过时的评论。仅供参考。

以上是关于强制 MySQL 在 Join 上使用两个索引的主要内容,如果未能解决你的问题,请参考以下文章

MySQL开发索引创建规范

MySQL 在结合 join 和 range 时不使用整个索引

为何阿里不推荐MySQL使用join?

在 MySQL 中强制使用索引进行连接的语法是啥

5.2 MySql数据库_索引规约

两个索引良好的表的单个 INNER JOIN 需要超过一分钟才能运行