最近遇到乙個sql執行很慢。這個sql比較長,但基本的形態比較簡單:
select t1.*, t2.c1 .... from t1 left join t2 on t1.c1 = t2.c1 left join t3 on t1.c2 = t3.c1 left join t3 on t1.c3 = t3.c1 ....
執行explain ,直接檢視執行計畫。發現很多的seqscan(表掃瞄)。其中有乙個表t3,被反覆連線,且每次連線都使用了表掃瞄:
先看看這張表的情況,實際上連線使用的都是c1欄位,而這個欄位上有索引,為什麼沒有使用?利用select count(*) from t3,看到此張表的資料量不大(13393),這可能是優化器為選擇索引的原因(資料量過小),但也可能是 統計資訊不準確造成,執行了analyze t3未見變化。(後來發現是這樣,當加入limit子句,且limit小於10000時,使用的是索引掃瞄,否則使用的是表掃瞄,這是由優化器根據代價評估決定的,並沒有問題。)
這裡,我們感覺執行計畫基本沒有問題。
用explain analyze 分析sql執行過程中,哪個環節耗費了最多時間。直接執行發現長時間得不到結果,所以加入了limit子句,即 explain analyze limit 1000,執行耗時8秒多。查詢計畫中處理了對t4等表的表掃瞄,但這些表資料量都不大。仔細檢視查詢計畫中的actual time的變化情況,發現在第二行出現了乙個不正常的現象:actual time=11.614..8913.433 。這個資料中,11.614代表輸出第一行時所用的時間,8913.433 **輸出所有行的時間,也就是說輸出第一行用了11ms,輸出1000行用了8.9秒!
檢視最後一行,發現了原因,這裡有個subplan,就是每次輸出一行前都要計算一下這個subplan,而這個subplan含有對t1的一次表掃瞄,就是說每次都要對hr_users全部掃瞄一次,返回1000條結果要掃瞄1000次t1。hr_users有2萬多條記錄,即總計掃瞄2000萬左右的記錄!
檢視sql,發現這個subplan對應乙個select裡面的子查詢,裡面有個條件是where to_char(t1.c10) = to_char(temp.c20)) ,而我們知道這種to_char轉換容易造成索引無法使用(user_id是數字型別)。將這個條件改為:t1.c10 = to_number(temp.c20)) (還需要業務評估是否可以這樣改!),上述語句的執行時間由8秒降低至100多ms!!
即與t2的連線過濾了很多記錄,懷疑是否這個連線是效能瓶頸。而t2是個view,於是試著把這個view的結果插入到乙個表中,並建立索引,使用這個表替換t2,發現作用不大。
為了防止統計資訊不準確,執行了如下的語句收集查詢中涉及的表的統計資訊:
t1;t2;
analyze t3;
analyze t4;
上述修改後,大幅降低了原sql的查詢延遲。直接執行這個sql結果超過數十萬行,因此進一步的優化,需要檢視這個sql是如何使用的,根據場景進行優化。
一次mysql優化經歷
某日運維突然說無線終端的頻道頁介面訪問量很大,memcache快取扛不過來,導致mysql併發查詢量太大,導致伺服器不停地宕機,只能不停地重啟機器。遺憾的是運維並沒有告訴mysql查詢量具體有多大 無量化,比如一秒多少個查詢 針對這個問題,有同事建議改了mysql memcache的架構,採用red...
一次優化經歷
問題 excel資料匯入,儲存到資料庫中,為了優化查詢效率和其他一些業務需求,需要將資料的一列屬性切分後儲存到redis中,插入資料庫前要保證其中乙個屬性不重複,另外乙個屬性已經在資料庫中。為了將問題描述簡單些,我們假設excel中有兩列資料a和b,其中資料a要保證資料庫中不重複,資料b保證資料庫中...
一次mysql優化經歷
某日運維突然說無線終端的頻道頁介面訪問量非常大,memcache快取扛只是來。導致mysql併發查詢量太大,導致server不停地宕機,僅僅能不停地重新啟動機器。遺憾的是運維並沒有告訴mysql查詢量詳細有多大 無量化,比方一秒多少個查詢 針對這個問題。有同事建議改了mysql memcache的架...