简体   繁体   English

添加其他约束后,MySQL停止使用索引

[英]MySQL stops using index when additional constraints are added

Using EXPLAIN reveals that the following query does not use my index, could somebody please explain what is going on? 使用EXPLAIN发现以下查询未使用我的索引,有人可以解释发生了什么吗?

    SELECT  u.id AS userId, firstName, profilePhotoId, preferredActivityId, preferredSubActivityId, availabilityType,
         3959 * ACOS(COS(radians(requestingUserLat)) * COS(radians(u.latitude)) * COS(radians(u.longitude) - radians(requestingUserLon)) + SIN(radians(requestingUserLat)) * SIN(radians(u.latitude))) AS distanceInMiles
    FROM users u
   WHERE u.latitude     between lat1    and lat2 -- MySQL 5.7 supports Point data type, but it is not indexed in innoDB. I store latitude and longitude as DOUBLE for now
     AND u.longitude    between lon1    and lon2
     AND u.dateOfBirth  between maxAge  and minAge -- dates are in millis, therefore maxAge will have a smaller value than minAge and so it needs to go first
     AND IF(gender       is null, TRUE, u.gender = gender)
     AND IF(activityType is null, TRUE, u.preferredActivityType = activityType)
     AND u.accountState = 'A'
     AND u.id != userId
  HAVING distanceInMiles < searchRadius ORDER BY distanceInMiles LIMIT pagingStart, pagingLength;


CREATE INDEX `findMatches` ON `users` (`latitude` ASC, `longitude` ASC, `dateOfBirth` ASC) USING BTREE;


The index is not used at all at this stage. 在此阶段根本不使用索引。 To get it to work, I need to comment out a bunch of columns from the SELECT statement, and also removed any unindexed columns from the WHERE clause. 为了使其正常工作,我需要从SELECT语句中注释掉一堆列,并从WHERE子句中删除所有未索引的列。 The following works: 以下作品:

    SELECT  u.id AS userId --, firstName, profilePhotoId, preferredActivityId, preferredSubActivityId, availabilityType,
         3959 * ACOS(COS(radians(requestingUserLat)) * COS(radians(u.latitude)) * COS(radians(u.longitude) - radians(requestingUserLon)) + SIN(radians(requestingUserLat)) * SIN(radians(u.latitude))) AS distanceInMiles
    FROM users u
   WHERE u.latitude     between lat1    and lat2 -- MySQL 5.7 supports Point data type, but it is not indexed in innoDB. We store latitude and longitude as DOUBLE for now
     AND u.longitude    between lon1    and lon2
     AND u.dateOfBirth  between maxAge  and minAge -- dates are in millis, therefore maxAge will have a smaller value than minAge and so it needs to go first
    -- AND IF(gender         is null, TRUE, u.gender = gender)
    -- AND IF(activityType is null, TRUE, u.preferredActivityType = activityType)
    -- AND u.accountState = 'A'
    -- AND u.id != userId
  HAVING distanceInMiles < searchRadius ORDER BY distanceInMiles LIMIT pagingStart, pagingLength;


Other things I tried: 我尝试过的其他方法:
I tried creating 3 distinct single-part indexes, in addition to my multi-part index that contains all 3 keys. 除了包含所有3个键的多部分索引之外,我还尝试创建3个不同的单部分索引。 Based on the docs here , shouldn't the optimizer merge them by creating a UNION of their qualifying rows, further speeding up execution? 根据此处的文档,优化器是否不应该通过创建其合格行的UNION来合并它们,从而进一步加快执行速度? It's not doing it, it still selects the multi-part (covering) index. 它没有这样做,它仍然选择多部分(覆盖)索引。


Any help greatly appreciated! 任何帮助,不胜感激!

This is a little difficult to explain. 这有点难以解释。

The query that uses the index is using it because the index is a "covering" index. 使用索引的查询正在使用它,因为该索引是“覆盖”索引。 That is, all the column in the index are in the query. 也就是说,索引中的所有列都在查询中。 The only part of the index really being used effectively is the condition on latitude . 真正有效使用的索引的唯一部分是latitude条件。

Normally a covering index would have only the columns mentioned in the query. 通常,覆盖索引包含查询中提到的列。 However, the primary key is used to reference the records, so I'm guessing that users.Id is the primary key on the table. 但是,主键用于引用记录,因此我猜到users.Id是表上的主键。 And the index is being scanned for valid values of latitude . 并且正在扫描索引以查找有效的latitude值。

The query that is not using the index is not using it for two reasons. 不使用索引的查询未使用索引有两个原因。 First, the conditions on the columns are inequalities. 首先,列上的条件是不等式。 An index seek can only use equality conditions and one inequality. 索引查找只能使用相等条件和一个不相等。 That means the index could only be used for latitude in its most effective method. 这意味着该索引只能以其最有效的方法用于latitude Second, the additional columns in the query require going to the data page anyway. 其次,无论如何查询中的其他列都需要转到数据页面。

In other words, the optimizer is, in effect, saying: "Why bother going to the index to scan through the index and then scan the data pages? Instead, I can just scan the data pages and get everything all at once." 换句话说,优化器实际上是在说:“为什么要麻烦索引以遍历索引然后再扫描数据页?相反,我可以只扫描数据页并立即获得所有内容。”

Your next question is undoubtedly: "But how do I make my query faster?" 毫无疑问,您的下一个问题是:“但是,如何使查询速度更快?” My suggestion would be to investigate spatial indexes . 我的建议是研究空间指数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM