關於海量數據表的分頁性能問題調試記錄(mysql left join的性能分析)

最近開發一個訂單系統,其中涉及訂單列表顯示的頁面,在數據量達到10萬級以上的時候,頁面刷新超級慢,幾乎可以打到30秒左右,而且點擊某個分頁也是反應及其慢,經過一段時間定位,發現問題所在,特此記錄下來供大家參考。

一、業務需求簡介:

涉及的表及其之間的關係如下:


要顯示的表爲訂單表(大概20萬條數據),同時每條記錄需要顯示客戶姓名和供貨商姓名,這樣就需要通過訂單表中的客戶ID/供貨商ID作爲外鍵分別訪問到客戶表和供貨商表,其中客戶表中有2條數據,供貨商表中有7條數據。

分頁顯示的訂單表每頁顯示30條數據,大概需要分6000多頁。此時每頁顯示特別慢。

二、問題分析:

        由於訂單列表頁面是通過GENEXUS工具生成的,生成的每一頁的MYSQL查詢語句經查看後爲類似如下形式:


    SELECT T1.ID,T1.xxx,T2.name as customer_name,T3.name as supplier_name 

    FROM ((`訂單表` T1 LEFT JOIN `客戶表` T2 ON T2.id=T1.客戶ID)  LEFT JOIN `供貨商表` T3 ON T3.id=T1.供貨商ID) LIMIT begin_row,end_row

        可見這個sql用了兩個LEFT JOIN把三張表聯合起來,其中訂單表(T1)是主表,使用LEFT JOIN的好處是當客戶ID或國貨商ID沒有對應的記錄時,該行訂單也會顯示出來,避免遺漏。

        上面這個SQL語句拿到SQL Manangerment Lite 裏面執行,並分析執行時間和查詢plan,發現這個sql語句執行了將近7-8秒,總查詢次數爲:T1記錄數*T2記錄數*T3記錄數,即使做了外鍵或索引,總查詢次數也至少爲2*T1記錄數。

        爲什麼會這樣?我們來看上面的LEFT JOIN的執行細節:MYSQL先是將T1與T2進行LEFT JOIN,這樣執行了將近20萬次(T1記錄數),生成一張臨時表Temp1(20萬條記錄),然後這張臨時表Temp1再與T3進行LEFT JOIN,生成一張臨時表Temp2(20萬條記錄),然後對這個臨時表Temp2進行分頁查詢(LIMIT語句),這樣的速度不慢纔怪!   

        然後又做了一個實驗,使用下面能起到同樣效果的SQL查詢語句進行查詢:

SELECT T1.ID,T1.xxx,T2.name as customer_name,T3.name as supplier_name  

FROM  (((SELECT ` 訂單表` T1  LIMIT begin_row,end_row ) LEFT JOIN `客戶表` T2 ON T2.id=T1.客戶ID)  LEFT JOIN `供貨商表` T3 ON T3.id=T1.供貨商ID) 

     注意LIMIT的位置。

    上面這個SQL執行飛快,毫秒級別,迅速返回了同樣的結果,上面這個SQL的執行細節爲:MYSQL顯示將T1進行分頁查詢(LIMIT語句找到某頁的30條記錄),查詢結果生成臨時表Temp1(30條記錄),然後對這個臨時表Temp1與T2進行LEFT JOIN,再進而與T3進行LEFT JOIN,總查詢次數也不到100次!

三、問題解決方案:

        找到問題的原因之後,就可以又針對性的給出解決方案,由於使用的是GENEXUS自動生成這部分代碼,所以先將GENEXUS中的GRID中刪除【客戶姓名】和【供貨商姓名】這兩個外鏈,同時過濾條件中也刪除這兩個外鏈的過濾,然後重新生成代碼,檢查生成代碼中已經不再又LEFT JOIN語句了,僅僅是簡單的SELECT FROM T1,此時運行WEB頁面,發現翻頁飛快。

        然後寫了兩個procedure,分別爲GetCustomerNameById(customer_id),GetSupplierNameById(supplier_id),從名字上即可知道是根據兩個id獲得名字的過程,在過程裏面分別查詢客戶表和供貨商表,返回姓名即可。然後定義兩個變量(客戶姓名/供貨商姓名)放到Grid裏並顯示出來,編譯運行後,發現在頁面上可以正常顯示客戶姓名和供貨商姓名,同時翻頁功能依然保持飛快!

至此問題解決!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章