[英]MySQL query faster in DESC order than ASC order
我制作了一個簡單的數據庫(innodb版本5.7.9),其中包含2個表post和post_tag。
帖子具有設置為主鍵的單個字段ID(大整數)(約120,000個條目)。 Post_tag有2個字段,post_id(大整數)和tag_id(整數),主鍵位於[post_id,tag_id]上。
以下查詢在〜1ms內運行:
SELECT
SQL_NO_CACHE p.id
FROM
post as p
STRAIGHT_JOIN
post_tag t
WHERE
t.post_id = p.id AND t.tag_id = 25
ORDER BY
p.id DESC
LIMIT 0, 100
但是,如果我將ORDER BY更改為ASC,它的運行速度將慢100倍! 我感興趣的那種...
知道為什么嗎?
最初,我希望ID對DESC進行排序,但我發現它比ASC慢。 我讀到,索引的自然排序是ASC,所以我還原了所有ID(通過執行ID = SOMETHING BIG-ID),但是由於它現在在ASC中的速度較慢,因此它沒有任何改變。
如果有用,我在這里上傳了數據庫。
在此先感謝任何可以提供幫助的人。
如果有“其他限制”,則所有下注均無效。
同時,看看你有什么...
STRAIGHT_JOIN
, USE INDEX
等是以下情況的拐杖:(a)您沒有“正確”的索引,或者(b)優化器無法確定“正確”的事情。 也就是說,尋找其他解決方案。
在您的示例中,最好使用普通的JOIN
和INDEX(tag_id, post_id)
。 這將post_tag
首先進入post_tag
因為有一個WHERE
子句可對其進行過濾。 優化器可能會看到t.post_id
和p.id
相同,因此在索引中以(25, post_id)
的結尾(對於DESC
)開始,然后進行掃描。 然后檢查,看看是否有一個post
進入(這是對於唯一明顯的使用post
-如果再有“其他方面的限制”,所有的賭注都關閉)。
所以,回到原來的問題。 STRAIGHT_JOIN
強制先查找post
。 但是25年代在哪里? 顯然接近年底 post_tag
。 因此,與從另一端開始掃描相比, ASC
需要更長的時間才能找到其中的100個(請參閱LIMIT
)!
假設這是一個多對多映射表,請執行以下操作:
CREATE TABLE post_tag (
post_id ...,
tag_id ...,
PRIMARY KEY(post_id, tag_id),
INDEX (tag_id, post_id)
) ENGINE=InnoDB;
我在博客中討論了許多原因。
如果按照建議的方式添加(tag_id, post_id DESC)
,請不要(tag_id, post_id DESC)
以為DESC
意味着什么-它可以識別,但可以忽略。 這兩部分都將存儲為ASC
。 將會發生的事情是,優化器足夠聰明,可以在25秒結束時開始並向后掃描。 這里是“證明”:
US
INDEX(state, population)
:
mysql> FLUSH STATUS;
mysql> SELECT city, population FROM US
WHERE state = 'OH'
ORDER BY population DESC LIMIT 5;
+------------+------------+
| city | population |
+------------+------------+
| Columbus | 736836 |
| Cleveland | 449514 |
| Toledo | 306974 |
| Cincinnati | 306382 |
| Akron | 208414 |
+------------+------------+
mysql> SHOW SESSION STATUS LIKE 'Handler%';
| Handler_read_key | 1 | -- get started at end of Ohio
| Handler_read_prev | 4 | -- read (5-1) more, scanning backwards
MySQL通過忽略INDEX
聲明中的DESC
來丟失船只的唯一情況是: ORDER BY a ASC, b DESC
無法使用INDEX(a,b)
。
大概您在post(id)
上有一個索引(例如,它是為主鍵自動創建的)。 當對ORDER BY
使用索引時,MySQL有時會注意索引的順序。
通過更改順序,您將以需要排序的方式更改查詢計划。
我建議僅使用一個表來編寫查詢:
SELECT t.post_id
FROM post_tag t
WHERE t.tag_id = 25
ORDER BY t.post_id DESC
LIMIT 0, 100;
假定post_id
所有值都引用有效的帖子(這似乎是一個非常合理的假設),則此查詢不需要JOIN
。
對於此查詢, post_tag(tag_id, post_id desc)
上的索引是最佳的,MySQL對於降序排序可能會做正確的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.