【问题标题】:MySQL compound index not used (intersection)未使用 MySQL 复合索引(交集)
【发布时间】:2014-12-19 17:49:04
【问题描述】:

我正在对 user_profile 表进行搜索,其中包含单个索引以及复合索引:

    SELECT ••• 
      FROM user_profile up
      JOIN auth_user au
        ON au.id = up.user_id 
      LEFT 
      JOIN _basecountry bc
        ON bc.id = up.country_id = 
      LEFT 
      JOIN _relationshipstatus rs
        ON rs.id = up.relationship_status_id 
      LEFT 
      JOIN _workstatus ws
        ON ws.id = up.work_status_id
      LEFT 
      JOIN _fieldofwork fw
        ON fw.id = up.field_of_work_id 
      LEFT 
      JOIN _fieldofstudy fs
        ON fs.id = up.field_of_study_id 
      LEFT 
      JOIN _educationlevel el
        ON el.id = up.education_level_id 
      LEFT 
      JOIN _religion r
        ON r.id = up.religion_id 
     WHERE up.lazy = 0 
       AND up.has_avatar = 1 
       AND up.inactive = 1 
       AND up.id <> 3247028 
       AND up.city = 'London' 
       AND up.challenge_count < 10 
       AND up.age BETWEEN 18 AND 28 
       AND up.gender = 'F' 
       AND up.id > 1468899 
     LIMIT 25

解释结果是:

POSSIBLE_KEYS PRIMARY,user_id,compound_match,age,gender,challenge_count,lazy,city,has_avatar,inactive

KEY city,lazy,has_avatar

KEY_LEN 578,1,1 无

行 1224

EXTRA 使用 intersect(city,lazy,has_avatar);在哪里使用

复合索引“compound_match”组合使用的列:id、user_id、age、gender、challenge_count、lazy、has_avatar、inactive

为什么 mysql 更喜欢 intersect 呢?结果查询很慢。

【问题讨论】:

  • 为清晰起见重新格式化查询。请注意,没有 ORDER BY 的 LIMIT 在很大程度上是没有意义的。而且您的解释似乎不完整

标签: mysql


【解决方案1】:

索引中列的顺序很重要。创建索引时应牢记以下规则(假设所有搜索词都与AND 组合):

  1. 平等条件中引用的列优先。这些顺序无关紧要,但我更喜欢从最有选择性到最不选择性列出它们。

  2. 下一个是在 range 条件中引用的 一个 列,或者是您用来排序或分组的一个或多个列。如果您有多个范围条件(就像您在此查询中所做的那样),抱歉,您只能期望其中一个列从索引中受益。所以选择最具选择性的列(即有助于以最佳比例缩小搜索范围)。

  3. 您既不搜索也不排序,但希望作为仅索引查询的一部分获取的其他列。但请记住 MySQL 索引中的最大列数是 16。

所以在这种情况下,你在 user_profile 表上有以下条件:

  • up.lazy = 0(平等)
  • up.has_avatar = 1(平等)
  • up.inactive = 1(平等)
  • up.id 3247028(范围)
  • up.city = '伦敦'(平等)
  • up.challenge_count
  • up.age BETWEEN 18 AND 28(范围)
  • up.gender = 'F'(平等)
  • up.id > 1468899(范围)

您没有进行排序(尽管正如@Strawberry 指出的那样,如果您使用 LIMIT,也许您应该这样做)。

您有其他列未用于搜索,但在连接条件中被引用:

  • up.user_id
  • up.country_id
  • up.relationship_status_id
  • up.work_status_id
  • up.field_of_work_id
  • up.field_of_study_id
  • up.education_level_id
  • up.religion_id

所以我会按以下顺序创建包含列的索引:

ALTER TABLE user_profile ADD INDEX
  (city, lazy, has_avatar, inactive, gender, /* equality conditions */
   id /* range */
   challenge_count, age, /* also in range conditions, but the index won't be used */
   user_id, country_id, relationship_status_id, work_status_id, field_of_work_id,
   field_of_study_id, education_level_id, religion_id /* covering index */
);

这是 16 列,是索引的最大值。如果您在选择列表中引用其他列,则会破坏覆盖索引优化,因此您不妨跳过所有这些额外的列。

我猜id 将是范围条件中最具选择性的列,但如果您认为 challenge_count 或 age 会更具选择性,请更改顺序。

您可能还喜欢我的演示文稿How to Design Indexes, Really

【讨论】:

  • 精通解释
  • 谢谢,确实很好的解释!当使用“城市”作为第一个字段时,它对我有用。但是,当使用 'country_id' (FK) 而不是 city 执行另一个查询时,不使用索引。相反,它使用:user_profile_d860be3c(country_id 的键名)、has_avatar、lazy、inactive、gender。
  • @jurer,当您在电话簿中查找某人时,如果您按姓氏查找他们真的很容易。但是,如果您按名字查找它们,电话簿的顺序就没有太大帮助了。想想为什么会这样。
猜你喜欢
  • 2011-06-03
  • 2014-03-23
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多