【问题标题】:Index design for query with foreign key and range外键和范围查询的索引设计
【发布时间】:2016-11-17 10:26:29
【问题描述】:

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

简化表如下(A和B都是大表,C是20行左右的小表,所有表都是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 上的索引没有被使用,而是它正在使用主键索引,并且这个查询将永远持续下去。强制使用我的索引似乎也无济于事。

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

【问题讨论】:

    标签: mysql sql indexing innodb


    【解决方案1】:

    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
    

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

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

    对于“派生”表公式,优化器“必须”以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 是多:多映射表,请特别注意有关该主题的部分。

    【讨论】:

    • 谢谢,它很有帮助。似乎优化器在使用哪个索引时并不总是在同一页面上。使用这些新索引,查询运行得更快但是我必须明确告诉它使用它们,否则它仍然会尝试使用主键在 A 上需要很长时间。
    最近更新 更多