MySQL limit與order by遇到的有趣問題 相信大家都知道MySQL的limit語法 select/delete/update .... limit start, len; 就是現在查詢或者更新幾條數據。當然我們需要知道如果是更新語句帶limit個,為了復制安全我們應該有 order by 主鍵/唯一鍵的語法,否則更新的行就不確定了。入正題,我這個場景是怎樣的呢? 業務反映有一條SQL突然查詢特別慢:select matchid,uid from usermatch_create where status='nostart' and matchid%2 = 0 order by matchid asc limit 1;我explain看了下執行計劃,也正常: explain select matchid,uid from usermatch_create where status='nostart' and matchid%2 = 0 order by matchid asc limit 1; +----+-------------+------------------+-------+----------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+-------+----------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | usermatch_create | index | idx_status_add | PRIMARY | 8 | NULL | 6 | Using where | +----+-------------+------------------+-------+----------------+---------+---------+------+------+-------------+ 但是執行起來確實相當的慢,將近2min的查詢,然後set profiling=on,然後查看到耗時最長的階段是sending data 這說明這個查詢過程中掃描的數據特別大導致大量的數據傳輸,為什麼會這樣呢?不是優化器預估是掃描6行嗎(當然這個值是預估的,不完全准確)?然後抱著嘗試的心態將order by matchid asc改為order by matchid desc,再次執行查詢,簡直就是秒殺,瞬間執行完,查看這次的執行計劃跟之前完全一樣。心裡當初還想btree對兩種order by的查詢有差異嗎?想來想去覺得原理上是一樣的,升序和降序只是掃描的方向不同。 接下來嘗試將where條件中的status條件去掉,發現兩條語句執行速度一樣快。此時大概明白問題就出在status這個判斷上了。猜測是由於數據分布的問題導致order by asc查詢特別慢,當時猜測在 order by matchid asc這個條件時,前面大量的行由於status不滿足‘nostart’這個條件所以相當於白掃描了很多行。而如果通過 order by matchid desc的話,在前面剛開始的幾行就找到status='nostart'的行,因此速度會很快。驗證方式: select status from usermatch_create where matchid%2 = 0 order by matchid asc limit 10000; 上面這個結果集顯示前面10000行都是status不為‘nostart’ select status from usermatch_create where matchid%2 = 0 order by matchid asc limit 10000; 上面這個結果集顯示前面10裡面就有status為‘nostart' 相信此時對於兩種order by的查詢時間為什麼會有這麼大的差異的原因就很清楚了。 那麼到底是什麼原因導致這種現象呢?為什麼以前沒有出現過這個查詢慢?因為這周由於機器原因這個業務少了一個從庫,為了減輕壓力,我要業務停了每天的刪除數據任務,而這個刪除任務會將很多status不為’nostart'的刪掉,因此刪掉之後也就不會導致之前說的那麼多沒有作用的掃描。至此問題得到較好的解決,只能說這個問題很有意思。