1. 程式人生 > >mysql高階(三)——sql調優

mysql高階(三)——sql調優

  • 小表驅動大表

    使用in的sql:select * from A where id in (select id from B)
    等價於:
    for select id from B
    for select * from A where A.id = B.id
    當B表的資料小於A表的資料集時,用in
    使用exists的sql:select * from A where exists (select 1 from B where B.id=A.id)
    等價於:
    for select id from A
    for select * from B where B.id = A.id
    當A表的資料小於B表的資料集時,用exists


    EXISTS:
    select …from table where exists (subquery)可以理解為:將主查詢的資料。放到子查詢中做條件驗證,根據驗證結果(true or false)來決定主查詢的資料是否得以保留
    提示:
    1.exists(subquery)只返回true或者false,因此子查詢的select *可以使select 1或其他,實際執行時候會忽略掉select清單,因此沒有區別
    2.exists子查詢的實際執行過程可能經歷了優化而不是我們理解上的逐條對比,如果擔憂效率問題,可進行實際檢驗以確認效率是否有問題
    3.exists子查詢往往也可以用條件表示式、其他子查詢或者join來代替

  • 索引排序問題(order by和group by類似)

    索引的作用,除了查詢還有一個就是排序
    這裡寫圖片描述

    可以看到Extra裡有一個Using filesort用來排序,mysql裡面排序的有filesort和索引自帶的排序,當然我們建立的有複合索引,如果能用到索引的排序,那麼效率將會提升很多,下面我們來看看怎麼用上索引排序
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述
    結論:where後面跟的索引和order by後面的排序欄位滿足最佳最字首原則,並且也是範圍之後就失效

    oder by後面的子段自己滿足左字首原則也可以
    這裡寫圖片描述
    這裡寫圖片描述


    這裡寫圖片描述
    結論:order by後面的欄位可以不考慮where用到的索引,自己滿足做字首法則來使用複合索引即可用到索引排序,那麼第三個為什麼沒用到呢?當用到name asc的時候,組合索引用的是升序,但是後面的age來也用這個索引排序的時候發現是升序用不了,就產生了個filesort的降序排序。證明如下:
    這裡寫圖片描述
    第一個name滿足做字首來使用複合索引的升序排序,第二個name也滿足做字首法則,使用了複合索引的降序排序,這樣其實用了兩次索引,一次用索引的升序,一次用降序
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述
    這兩個出現的原因就是,前面where已經建立了一個索引,age>20導致索引age後面的pos失效,但是order by從age開始的排序還是可以用的;但是order by pos失效(因為pos失效了),索引圖二產生了filesort;圖三,order by age pos雖然pos失效了,但是再從age開始,pos也又可以用了


    結論:我們得出首先where後面如果用上索引會開一個索引來查詢,下一個欄位如果滿足條件就接著用這個索引來查詢;到了order by後面的欄位如果能和where後的欄位滿足做字首法則就可以接著用where後面開的的索引來進行排序;否則就自己在開一個(升序或者降序)索引來排序,如果order by有多個欄位也是一樣的,能用到前面欄位開的索引就用,否則自己就在開一個
    這裡寫圖片描述

  • sql慢查詢分析

    慢查詢日誌開啟方法一:
    在配置檔案my.cnf或my.ini中在[mysqld]一行下面加入兩個配置引數
    log-slow-queries=/data/mysqldata/slow-query.log
    long_query_time=2
    注:log-slow-queries引數為慢查詢日誌存放的位置,一般這個目錄要有mysql的執行帳號的可寫許可權,一般都將這個目錄設定為mysql的資料存放目錄;
    long_query_time=2中的2表示查詢超過兩秒才記錄;
    在my.cnf或者my.ini中新增log-queries-not-using-indexes引數,表示記錄下沒有使用索引的查詢。
    log-slow-queries=/data/mysqldata/slow-query.log
    long_query_time=10
    log-queries-not-using-indexes
    慢查詢日誌開啟方法二:
    我們可以通過命令列設定變數來即時啟動慢日誌查詢。由下圖可知慢日誌沒有開啟,slow_launch_time=# 表示如果建立執行緒花費了比這個值更長的時間,slow_launch_threads 計數器將增加
    這裡寫圖片描述
    設定慢日誌開啟
    這裡寫圖片描述
    MySQL後可以查詢long_query_time 的值
    這裡寫圖片描述
    為了方便測試,可以將修改慢查詢時間為5秒
    這裡寫圖片描述
    執行select sleep(6)睡眠6秒,在慢查詢日誌檔案下面就有一條記錄了


    當然mysql自帶一個分析工具,具體命令見下:
    這裡寫圖片描述
    這裡寫圖片描述

  • show profile

    檢視命令:show variables like ‘profiling%’;預設關閉,使用前需要開啟;
    開啟命令:set profiling=on;
    開啟之後,執行sql會被mysql記錄
    檢視執行sql記錄結果(帶有sql的id和執行時間):Show profiles
    診斷sql :show profile cpu, block io for query 問題sql的id;

    日常開發需要注意的結論:
    converting HEAP to MyISAM : 查詢結果太大,記憶體都不夠用了,往磁碟上搬了;
    creating tmp table :建立臨時表,拷貝資料到臨時表,然後再刪除;
    copying to tmp table on disk :把記憶體中臨時表複製到磁碟,危險!!!
    locked
    注:以上四個中若出現一個或多個,表示sql 語句 必須優化。

輸出內容裡面的欄位表示含義:
ALL: 顯示所有的開銷資訊
BLOCK IO : 顯示塊IO相關開銷
CONTEXT SWITCHS: 上下文切換相關開銷
CPU : 顯示cpu 相關開銷
IPC: 顯示傳送和接收相關開銷
MEMORY: 顯示記憶體相關開銷
PAGE FAULTS:顯示頁面錯誤相關開銷資訊
SOURCE : 顯示和Source_function ,Source_file,Source_line 相關的開銷資訊
SWAPS:顯示交換次數相關的開銷資訊
Status : sql 語句執行的狀態
Duration: sql 執行過程中每一個步驟的耗時
CPU_user: 當前使用者佔有的cpu
CPU_system: 系統佔有的cpu
Block_ops_in : I/O 輸入
Block_ops_out : I/O 輸出