2014-06-12 72 views
2

使用EXPLAIN顯示,以下查詢不使用我的索引,可能會有人請解釋一下到底是怎麼回事?MySQL的停止使用索引時,附加的約束添加

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來合併它們,從而進一步加快執行速度?它沒有這樣做,它仍然選擇多部分(覆蓋)索引。


任何幫助,不勝感激!

回答

0

這有點難以解釋。

使用該索引的查詢,因爲該指數是一個「覆蓋」指標在使用它。也就是說,索引中的所有列都在查詢中。索引中唯一有效的部分是latitude

正常情況下,覆蓋索引將只有只有查詢中提到的列。但是,主鍵用於引用記錄,所以我猜users.Id是表中的主鍵。正在掃描索引的有效值latitude

未使用索引的查詢不使用它的原因有兩個。首先,列上的條件是不平等的。索引查找只能使用相等條件和一個不等式。這意味着該索引只能用於latitude其最有效的方法。其次,查詢中的其他列無論如何都需要轉到數據頁面。

換句話說,優化器實際上是這樣說的:「爲什麼要去索引來掃描索引然後掃描數據頁?相反,我可以掃描數據頁面並獲取所有內容一旦。」

你的下一個問題無疑是:「但我怎麼做我的查詢速度更快?」我的建議是調查spatial indexes

+0

謝謝戈登。從MySQL v5.7開始,[InnoDB支持空間數據類型,但不支持空間索引。](http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html)讓我們忘記那些列包含空間信息,我們將它們視爲「DOUBLE」。運行'EXPLAIN',我可以看到註釋條件列'dateOfBirth',並且下一步'經度'會逐漸減少報告的索引關鍵字長度。我認爲這意味着整個覆蓋索引被有效使用,而不僅僅是「緯度」。你怎麼看? – DTs

+0

@DTs。 。 。覆蓋索引可能仍然只用於獲取列。掃描索引可能比掃描表更快。 –

+0

仍然困惑。我得到的是讀取非索引列所需的數據頁面讀取,但是如果報告的索引關鍵字長度顯示索引的所有3列都被使用,那麼索引的「longitude」和「dataOfBirth」部分如何不被使用用於查找合格行的相應PK?我試圖在文檔中找到你提到的限制_「索引查找只能使用平等條件和一個不等式。」_,你有任何鏈接方便嗎?謝謝。 – DTs