如何優化一條 order by 的 SQL 語句?
數據結構如下圖(表引擎myisam):
現有一條語句:explain SELECT sql_no_cache id, wordFROM house_keyword
WHERE (city = bjOR city = cnOR city = all)AND `word` LIKE 你好%ORDER BY user_count DESC LIMIT 10結果如下:疑問???
此處為什麼還會出現額外的排序呢?(using filesort)
user_count 沒有索引的原因。mysql在處理這種查詢的時候,都是file sort
你想用一個複合索引解決如上所有問題,又是or ,又是 like,索引沒那麼智能
我對索引的理解有一個最簡單的思路,非常好用,就是你把這些索引欄位(複合索引的按照順序粘在一起),當做是一個有序的序列,然後你琢磨,查找的時候,能否有二分法和連續區間命中,如果能,索引就體現效率,如果不能,那麼它會查找多少範圍?比如 city=bj and word=你好 order by user_count 這個是對的,是避免file_sort的。 樓上黃勇對索引的理解顯然是不太對的。
如果是 city=bj and word like 你好%『 order by user_count 雖然不能完全避免存在多餘的數據來排序,但是,基本上,效率還是有保證的。這個SQL,我個人評估,雖然用到了 filesort ,但索引依然會發揮效率,通常問題不大。
優化的目標是適當控制 like 命中集。比如禁止 like空。如果在city=。。。的範疇內,word like的命中集太多,排序效率會有所降低。
我一般不會苛求索引效率最優化,看應用場景而定。為什麼不出現排序?
這個例子挺有意思的,涉及到很多背景知識。比如,基數對 SQL 優化器/解釋器的影響,優化器對 OR 條件的解釋, LIKE 語句,ORDER BY 對排序的影響,降序是否可以優化,索引創建是否合理? 是否有效率...
還有,欄位類型的選擇(貌似有點問題)。我來翻譯一下樓主的題目:
1) 我有一個index, 名字叫city, 欄位是 city, word, user_count2) 我有一個sql 語句, 肉眼看到的是, where 裡面用到city 和 word, order裡面用到user_count3) 在1) 2) 的前提下, 為蝦米還要用到using filesort咧?我來解答一下樓主的困惑, 如果我說錯了, 請高人指正, 同時說一下我的理解1) clustered index, 在mysql裡面是不能指定的, primary key 默認就是clustered index, 換句話說, 如果你沒有設置primary key, 那麼, 這個table是沒有clustered index的
2) 分開來看, 如果where 裡面只有city, 肯定是用到index的, 如果是where 裡面有city 和 word, 不管他們的順序如何, 也是可以用到索引的, 問題就在於為啥where 裡面有city 和 word, order 裡面有user_count 就需要用到using filesort列? 其實很簡單你自己畫一顆BTree, 或者閉上眼睛想一下, 你覺得index的數據結構是怎麼樣的, 模擬一下就知道為啥了.我不是個高手,但自認為看懂了這個問題,首先我來舉例說明一下為什麼會出現filesort。
首先得了解索引在硬碟中的存儲狀態,複合索引的保存是按照排列組合的順序排好的,(`city`,`word`,`user_count`)這種索引,他保存的順序你可以抽象的理解為(acity,aword,auser_count)(acity,aword,buser_count)(acity,bword,auser_count)(acity,bword,buser_count)(bcity,aword,auser_count)(bcity,aword,buser_count)
(bcity,bword,auser_count)(bcity,bword,buser_count)每一個括弧就是一條記錄。這樣一種存儲狀態,複合索引中之所以where中不包含第一個欄位的時候用不上索引,就是因為這種存儲結構決定了去掉第一個欄位來看,對第二個欄位和第三個欄位完全是亂序的,你沒有辦法用二分法快速地定位到所需要的元素。只有在排序好的數據上才能用上二叉樹或者是多叉樹的快速搜索。再來看看樓主的索引順序是,`city`,`word`,`user_count`,搜索時`city`和`word`肯定是用上索引了,orderby的欄位加入到索引中 所以確定了city 和word 之後的數據是按照user_count排好序的,mysql用不著再進行排序了。 等於是三個欄位都用上了索引。沒有比這個更好的了。你獲得的數據本來就是按序排序的
auser_countbuser_count但這裡有一個變數是用上了模糊搜索,用了模糊搜索以後,這裡就會有一個問題產生:
你可以想像以你好開頭的記錄不止一條,所以索引里的數據是這樣的。
(ncity, 你好a,auser_count)
(ncity, 你好a,buser_count)(ncity, 你好b,auser_count)(ncity, 你好b,buser_count)所以按照word的索引找出數據以後,得到的user_count的數據是
auser_countbuser_countauser_countbuser_count這個樣子的,當然你不能把第一行的auser_count和第三行的auser_count當成一樣的值,這只是為了說明原理。mysql得到這樣一堆數據以後肯定還需要再進行排序,這就是filesort出現的原因。
所以前兩個索引用上了,但最後一個沒有用上,這看起來沒什麼好辦法解決。劉項說用上了索引,那是因為只搜了索引的那幾個欄位,如果搜其他的欄位就用不上索引了,而且這裡的索引只意味著是用了索引來搜索數據,並沒有告訴你效率是否就是高的。這種方式的效率估計只是比直接查資料庫高那麼一點?"city = bj and word=你好 order by user_count就可以避免filesort了",這個也不對,city這個索引建立時又不是按user_count大小順序建立的,怎麼可能會在輸出查詢結果時不排序呢?這個概念從哪裡獲得的?
第一眼的反應是這個SQL真是複雜。
再仔細看,三個欄位有建多列索引,LIKE 用的是xxx%,而不是『%xx%』,嗯,貌似也會用到索引。然後題主問,為什麼會using filesort?為什麼不會using filesort!?
我把題目簡化一下,把中間那個LIKE去掉。假如有建一個city和user_count兩個欄位的多列索引,SQL語句假如是:
explain SELECT sql_no_cache id, wordFROM house_keyword
WHERE (city = bj OR city = cn OR city = all)ORDER BY user_count DESC LIMIT 10就算是這樣,都會出現using filesort的啊!曾經為了優化 WHERE xx1 IN (a,b,c,d,e,f,g) ORDER BY xx2 這樣的語句頭疼了好久,在Mysql下面,這個...基本上...很難。排序沒看太明白,我在想這個where:
SELECT sql_no_cache id, wordFROM house_keywordWHEREcity in(bj,cn,all) AND left(word,2)=你好
ORDER BY user_count DESC LIMIT 10不知道和上面那個哪個效率高些?explain
樓主,應該邀請zhihu上的mysql DBA來這個問題。
推薦閱讀:
※產品DBA、開發DBA、運維DBA的區別?
※MYSQL及MySQL WORKBENCH安裝過程遇到的問題及處理方法
※大家設計資料庫時使用外鍵嗎?
TAG:MySQL |