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

时间:2014-06-12 22:18:01

标签: mysql sql indexing query-optimization

使用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;


在此阶段根本不使用该索引。为了使它工作,我需要从SELECT语句中注释掉一堆列,并从WHERE子句中删除任何未编制索引的列。以下作品:

    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;


我试过的其他事情:
除了包含所有3个键的多部分索引之外,我尝试创建3个不同的单部分索引。基于文档here,优化器不应该通过创建符合条件的行的UNION来合并它们,从而进一步加快执行速度吗?它没有这样做,它仍然选择多部分(覆盖)索引。


任何帮助非常感谢!

1 个答案:

答案 0 :(得分:0)

这有点难以解释。

使用索引的查询正在使用它,因为索引是“覆盖”索引。也就是说,索引中的所有列都在查询中。真正被有效使用的唯一部分是latitude上的条件。

通常,覆盖索引只有 查询中提到的列。但是,主键用于引用记录,因此我猜测users.Id是表中的主键。并且正在扫描索引的有效值latitude

由于两个原因,未使用索引的查询未使用它。首先,列的条件是不等式。索引搜索只能使用平等条件和一个不等式。这意味着索引只能用于latitude最有效的方法。其次,查询中的其他列无论如何都需要转到数据页。

换句话说,优化器实际上是在说:“为什么要去索引扫描索引然后扫描数据页呢?相反,我可以只扫描数据页并一次性获取所有内容。“

您的下一个问题无疑是:“但如何让我的查询更快?”我的建议是调查spatial indexes