9
explain
select
    *
from
    zipcode_distances z 
inner join
    venues v    
    on z.zipcode_to=v.zipcode
inner join
    events e
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

我正在尝试查找所有“在邮政编码 92108 5 英里范围内的场所的活动”,但是,我很难优化此查询。

这是解释的样子:

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra

1, SIMPLE, e, ALL, idx_venue_id, , , , 60024, 
1, SIMPLE, v, eq_ref, PRIMARY,idx_zipcode, PRIMARY, 4, comedyworld.e.venue_id, 1, 
1, SIMPLE, z, ref, idx_zip_from_distance,idx_zip_to_distance,idx_zip_from_to, idx_zip_from_to, 30, const,comedyworld.v.zipcode, 1, Using where; Using index

我正在对“e”表进行全表扫描,但我无法弄清楚我需要创建什么索引才能使其快速运行。

任何意见,将不胜感激

谢谢

4

4 回答 4

11

根据EXPLAIN您问题中的输出,您已经拥有了查询应该使用的所有索引,即:

CREATE INDEX idx_zip_from_distance
  ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);

(我不确定您的索引名称是否idx_zip_from_distance真的包含该zipcode_to列。如果没有,您应该添加它以使其成为覆盖索引。此外,为了完整性,我已经包含了该venues.ididx_zipcode,但是,假设它是主键该表并且您正在使用 InnoDB,无论如何它都会自动包含在内。)

然而,看起来 MySQL 选择了一个不同的、可能不是最优的查询计划,它扫描所有事件,找到它们的地点和邮政编码,然后才过滤距离结果。如果事件表的基数足够低,这可能是最佳查询计划,但从您提出这个问题的事实来看,我认为不是。

次优查询计划的一个原因可能是您有太多索引,这会使计划者感到困惑。例如,考虑到它存储的数据可能是对称的,您真的需要 zipcode 表上的所有这三个索引吗?就个人而言,我建议只使用我上面描述的索引,加上一个唯一索引(如果你没有人工索引,它也可以是主键)(zipcode_to, zipcode_from)(最好按这个顺序,这样任何偶尔的查询zipcode_to=?都可以利用它)。

但是,根据我所做的一些测试,我怀疑 MySQL 选择错误查询计划的主要问题仅仅是表的相对基数。据推测,您的实际zipcode_distances表是巨大的WHERE,而 MySQL 还不够聪明,无法意识到子句中的条件真正缩小了多少。

如果是这样,最好和最简单的解决方法可能是简单地强制 MySQL 使用您想要的索引

select
    *
from
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance)
inner join
    venues v    
    FORCE INDEX (idx_zipcode)
    on z.zipcode_to=v.zipcode
inner join
    events e
    FORCE INDEX (idx_venue_id)
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

使用该查询,您确实应该获得所需的查询计划。(你确实需要FORCE INDEX在这里,因为只有USE INDEX查询规划器仍然可以决定使用表扫描而不是建议的索引,这违背了目的。我第一次测试时发生了这种情况。)

附言。这是一个关于 SQLize 的演示,无论没有 ,都FORCE INDEX演示了这个问题。

于 2013-01-01T01:28:33.913 回答
1

是否为两个表中的列建立了索引?

e.id and v.venue_id

如果不这样做,则在两个表中创建索引。如果您已经拥有,则可能是您在一个或多个表中的记录很少,并且分析器检测到执行完整扫描而不是索引读取更有效。

于 2012-12-31T23:47:05.200 回答
0

您可以使用子查询:

select * from zipcode_distances z, venues v, events e
where
    z.id in (select id from zipcode z where z.zipcode_from='92108' and z.distance <= 5)
    and z.zipcode_to=v.zipcode
    and v.id=e.venue_id
于 2012-12-31T23:32:58.830 回答
-1

您正在从所有表中选择所有列,(select *)因此当查询引擎必须在每一行上从索引到表进行查找时,使用索引的优化器几乎没有意义。

于 2013-01-01T07:28:54.103 回答