强制 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 上使用两个索引的主要内容,如果未能解决你的问题,请参考以下文章