記一次 MySQL 查詢優化

最近在學習 MySQL 查詢優化的知識,正在閱讀簡朝陽編寫的《MySQL性能調優與架構設計》,受益良多。sql

做者先介紹了 MySQL 的基礎知識,好比 MySQL 的架構組成,有哪些存儲引擎,不一樣存儲引擎的數據表會有什麼樣的物理文件,它的邏輯模塊組成有哪些等等。性能優化

接着做者就談到了 MySQL 的性能優化,討論了影響 MySQL 性能的相關因素有哪些,MySQL 中的鎖定機制,MySQL 的索引類型,一些類型的查詢的優化技巧及原則等等。服務器

這本書還沒看完,還在繼續學習中 ...架構

昨天同事說有個查詢語句很慢,查詢一次要 40 多秒,問我有沒有時間幫看看怎麼優化。我固然樂意了,正好能夠用來練練手,看學習到的知識有沒有用。通過不斷的優化以後,那條語句被我優化到了 0.03 秒的速度,速度提升了幾個數量級。性能

這更加堅決了我繼續學習 MySQL 查詢優化的信心!學習

如下是記錄整個語句優化的過程:優化

  • 剛開始的慢查詢
SELECT 
    b.headimgurl
FROM
    bdtt_view_share_log_11 AS a
        LEFT JOIN
    mdrt_weixin_mp_user AS b ON a.unionid = b.unionid
WHERE
    a.author_unionid = 'opBomw1reHNZhG_X2c4a1W2y4N1o'
        AND a.use_type = 0
        AND a.type = 3
GROUP BY a.unionid
ORDER BY RAND()
LIMIT 8;

這條查詢語句涉及到兩張表,bdtt_view_share_log_11mdrt_weixin_mp_user ,它們的數據量分別是 250 w 和 2100 w 的級別;字段 unionidauthor_unionid 都建有索引。url

剛拿到這條查詢語句,第一感受就是有 rand() 隨機排序,還有對 unionid 進行分組操做。rand() 隨機排序是公認的比較難以優化的,它是沒法使用索引的。group by 若是可以用得上索引的話,狀況並不會太糟糕。還有一個 LEFT JOIN 關聯查詢,這個也是可能會出現性能問題的點,但仍是會有必定的優化套路可用的。spa

以上是一個主觀的判斷,實際狀況是什麼樣的,得看看大名鼎鼎的 EXPLAIN架構設計

q

圖爲 MySQL 查詢計劃的可視化圖

從圖中能夠看出,確實和剛開始的預感同樣,性能的瓶頸在於 rand() 的隨機排序。MySQL 須要對 38 w 的數據進行隨機排序,有多慢可想而知了!這裏有一個能夠優化的地方,根據小結果集驅動大結果集的原則,咱們能夠先將 a 表使用條件進行過濾以後,在將其結果集與 b 表進行 JOIN

  • 根據小結果集驅動大結果集的原則優化
SELECT 
    b.headimgurl
FROM
    (SELECT 
        unionid
    FROM
        bdtt_view_share_log_11
    WHERE
        author_unionid = 'opBomw1reHNZhG_X2c4a1W2y4N1o'
            AND use_type = 0
            AND type = 3
    GROUP BY unionid
    ORDER BY RAND()
    LIMIT 8) a
        LEFT JOIN
    mdrt_weixin_mp_user AS b ON a.unionid = b.unionid
LIMIT 8;

通過這樣子的優化以後,這條語句的查詢時間已經減小到 1 秒多了,它的執行計劃圖以下:

q

能夠從圖中看出,通過優化以後,與 b 進行關聯查詢的結果集只有 8 條數據而已了,而不是剛開始的 38 w 條數據進行關聯,這使得查詢時間大大的下降了。

但是,這查詢還須要 1 秒多時間呢,這在生產環境中也仍是不能忍受的啊!

如今的關鍵點仍是在於 rand() 這個隨機排序這裏。在網上查找相關的解決方案,大部分的都是先在程序中隨機生成必要的主鍵,而後根據主鍵就能找到隨機的記錄了;要麼就是生成一個小於最大值數,而後從那個數開始取連續的幾條數據。這些在這條查詢中都無論用!

後來我就取了一個折中的方案,不從全局中去隨機數據,只是從部分結果集中取隨機的 8 條數據,這樣參與隨機排序的數據就能夠控制在必定範圍內了。何況,這裏的隨機的結果只是給用戶一個不同的感受而已,因此這樣子作並不會有什麼多大的不妥。

還有一點,這裏的 group by 可使用 distinct 來取代,由於 group by 還會多一個排序的操做,而咱們不須要排序。

  • 折中的隨機排序方案
SELECT DISTINCT
    b.headimgurl
FROM
    (SELECT DISTINCT
        unionid
    FROM
        bdtt_view_share_log_11
    WHERE
        author_unionid = 'opBomw1reHNZhG_X2c4a1W2y4N1o'
            AND use_type = 0
            AND type = 3
    LIMIT 100) a
        LEFT JOIN
    mdrt_weixin_mp_user AS b ON a.unionid = b.unionid
ORDER BY RAND()
LIMIT 8;

這樣已經優化到 0.03 秒了。如下是執行計劃圖:

s

能夠看到,最後參與隨機排序的結果集也就百來條而已了,MySQL 服務器處理這點數據應該仍是挺快的吧。

到這裏,這條慢查詢的優化就暫時告一段落了。

最後,繼續學習,優化永無止境!!

相關文章
相關標籤/搜索