带外键和范围查询的索引设计

Index design for query with foreign key and range

我正在使用 MySQL 5.6,在一些非常大的 table 中查询性能遇到了一些问题。具体来说,当 table 和 in() 条件之间存在引用时,我不确定如何正确索引我的 table。

简化的table如下(A和B都是巨大的table而C是一个小的table,大约有20行,都是table是 InnoDB)

A(id int, created datetime, val int)

B(id int, a_id int, c_id int)

C(id int, val int)

有问题的查询看起来像这样:

Select  a.id
    from  a
    join  b  ON (a.id = b.a_id)
    where  a.created >= now() - interval 90 day
      and  a.val = 0
      and  b.c_id in (
        SELECT  id
            from  c
            where  val = 1 ) 

我在 A 上创建了一个索引 (val, created, id) ,在 B 上创建了一个索引 (c_id, a_id) ,当上有 '=' 条件时效果很好c_id(例如 c_id = 5)但是在 'in()' 条件下,解释告诉我 A 上的索引没有被使用,而是使用主键索引,这查询将永远进行。强制使用我的索引似乎也无济于事。

关于如何更好地索引此查询或以其他方式提高此类查询的性能的任何提示或想法?

IN ( SELECT ... ) 效率低于 JOIN.

Select  a.id
    from  a
    join  b  ON (a.id = b.a_id)
    JOIN  c  ON b.c_id = c.id
    where  a.created >= now() - interval 90 day
      and  a.val = 0
      and  c.val = 1 

索引:

A:  INDEX(val, created) -- in that order
B:  INDEX(a_id, c_id)   -- in that order; "covering"
C:  Nothing new needed, assuming you have PRIMARY KEY(id) and InnoDB

(编辑)索引假定 table 将按以下顺序完成:A、B、C。很可能会这样,因为... A 可能有WHERE 中的最佳选择性。很明显,B,然后是C。因此,我对 B.

的索引排序

假设 A 的 PK 是 (id),那么 INDEX(val, created) 等同于 INDEX(val, created, id)(如您所建议的)。

使用 'derived' table 公式,优化器 'must' 从 C 开始,然后转到 B,最后 A:

C:  INDEX(val, id)     -- (again, `id` optional)
B:  INDEX(c_id, a_id)  -- as you stated
A:  Given that it has `PRIMARY KEY(id)` and is InnoDB, no index is useful.

由于无法在a.val和a.created上进行过滤,我预测即使是这个公式也会比我的慢:

Select  a.id
    FROM  ( SELECT  id  FROM  C  WHERE  val = 1 ) AS cx
    JOIN  B  ON b.c_id = cx.id
    JOIN  A  ON (a.id = b.a_id)
    where  a.created >= now() - interval 90 day
      and  a.val = 0 

Index Cookbook。如果 B 是 many:many 映射 table,则请特别注意有关该主题的部分。