SQL語句的優化

SQL語句的優化

如何索取有性能問題SQL的渠道

  1. 通過用戶反饋獲取存在性能問題的SQL

  2. 通過慢查日誌獲取存在性能問題的SQL

  3. 實時獲取存在性能問題的SQL

慢查詢日誌介紹

  • slow_quey_log=on 啓動記錄慢查詢日誌
  • slow_query_log_file 指定慢查詢日誌的存儲路徑及文件(默認情況下保存在MySQL的數據目錄中)
  • long_query_time 指定記錄慢查詢日誌sql執行的閾值(默認爲10秒,通常改爲0.001秒比較合適)
  • log_queries_not_using_indexes 是否記錄未使用索引的SQL

    set global sql_query_log=on;

    sysbench --test=./oltp.lua --mysql-table-engine=innodb --oltp-table-size=10000 --mysql-db=tests --mysql-user=sbtest --mysql-password=123456 --oltp-tables-count=10 --mysql-socket=/usr/local/mysql/data/mysql.sock run

慢查詢日誌分析工具

mysqldumpslow
  • 彙總除查詢條件外其它完全相同的SQL並將分析結果按照參數中所指定的順序輸出

    mysqldumpslow -s r -t 10 slow-mysql.log

    -s order(c,t,l,r,at,al,ar)[指定按照哪種排序方式輸出結果]

    1. c按照查詢的次數排序
    2. t按照查詢的總時間排序
    3. l按照查詢中鎖的時間來排序
    4. r按照查詢中返回總的數據行來排序
    5. at、al、ar平均數量來排序
      • t top[指定取前幾條作爲結束輸出]
pt-query-digest

pt-query-digest \

--explain h=127.0.0.1,u=root,p=p@ssWord \

slow-mysql.log

pt-query-digest –explain h=127.0.0.1 slow-mysql.log > slow.rep

實時獲取存在性能問題的SQL

select id,user,host,db,command,time,state,info
FROM information_schema.processlist
WHERE time>=60

查詢速度爲什麼會這麼慢?

  1. 客戶端發送SQL請求給服務器
  2. 服務器檢查是否可以在查詢緩存中命中該SQL
  3. 服務器端進行SQL解析,預處理,再由優化器生成對應的執行計劃
  4. 根據執行計劃,調用存儲引擎API來查詢數據
  5. 將結果返回給客戶端

》 對於一個讀寫頻繁的系統使用查詢緩存很可能會降低查詢處理的效率,建議大家不要使用查詢緩存

2.其中涉及的參數:
    query_cache_type 設置查詢緩存是否可用[ON,OFF,DEMAND]

        DEMAND表示只有在查詢語句中使用了SQL_CACHE和SQL_NO_CACHE來控制是否需要進行緩存

    query_cache_size 設置查詢緩存的內存的大小

    query_cache_limit 設置查詢緩存可用的存儲的最大值(加上SQL_NO_CACHE可以提高效率)         

    query_cache_wlock_invalidate 設置數據表被鎖後是否返回緩存中的數據

    query_cache_min_res_unit 設置查詢緩存分配的內存塊最小單位

3.MySQL依照這個執行計劃和存儲引擎進行交互
    解析SQL,預處理。優化SQL的查詢計劃

    語法解析階段是通過關鍵字對MySQL語句進行解析,並生成一顆對應的解析樹
        MySQL解析器將使用MySQL語法規則驗證和解析查詢,包括檢查語法是否使用了正確的關鍵走;關鍵字的順序是否正確等等;

    預處理階段是根據MySQL規則進一步檢查解析樹是否合法 
        檢查查詢中所涉及的表和數據列是否存在及名字或別名是否存在歧義等等
        語法檢查通過了,查詢優化器就可以生成查詢計劃了

    優化器SQL的查詢計劃階段對上一步所生成的執行計劃進行選擇基於成本模型的最優的執行計劃【下面是影響選擇最優的查詢計劃的7因素】
        1.統計信息不準確
        2.執行計劃中的成本估算不等於實際的執行計劃的成本
        3.MySQL優化器認爲的最優的可能與你認爲最優的不一樣【基於成本模型選擇最優的執行計劃】
        4.MySQL從不考慮其他的併發的查詢,這可能會影響當前查詢的速度
        5.MySQL有時候也會基於一些固定的規則來生成執行計劃
        6.MySQL不會考慮不受其控制的成本
    查詢優化器在目前的版本中可以進行優化的SQL的類型:
        1.重新定義表的關聯順序
        2.將外連接轉化爲內連接
        3.使用等價變換規則
        4.優化count(),min()和max()[select tables optimozed away]
        5.將一個表達式轉化爲一個常數表達式
        6.子查詢優化
        7.提前終止查詢
        8.對in()條件進行優化

如何確定查詢處理各個階段所消耗的時間

  • 使用profile[不建議使用,未來mysql中將被移除]

    1. set profiling = 1;[啓動profile,這是一個session級別的配置]
    2. 執行查詢
    3. show profiles;[查看每一個查詢所消耗的總的時間的信息]
    4. show profile for query N;[查詢的每個階段所消耗的時間]
    5. show profile cpu for query N;[查看每個階段所消耗的時間信息和所消耗的cpu的信息]
  • 使用performance_schema

    1. 啓動所需要的監控和歷史記錄表的信息

      update setup_instruments set enabled=’yes’,timed=’yes’ where name like ‘stage%’;

      update setup_consumers set enabled=’yes’ where name like ‘events%’;

    2. SELECT
      a.thread_id,
      sql_text,
      c.event_name,
      (c.timer_end - c.timer_start) / 1000000000 AS ‘duration(ms)’
      FROM
      events_statements_history_long a
      JOIN threads b on a.thread_id=b.thread_id
      JOIN events_stages_history_long c ON c.thread_id=b.thread_id
      AND c.event_id between a.event_id and a.end_event_id
      WHERE b.processlist_id=CONNECTION_ID()
      AND a.event_name=’statement/sql/select’
      ORDER BY a.thread_id,c.event_id

特定的SQL查詢優化

  • 大表的更新和刪除

    delimiter $$
    		use 'imooc'$$
    drop procedure if exists 'p_delete_rows'$$
    		create definer='root'@'127.0.0.1' procedure 'p_delete_rows'()
    		begin 
    		declare v_rows int;
    		set v_rows int,
    		while v_rows=1,
    		while v_rows>0
    		do 
    			delete from test where id>=9000 and id<=19000 limit 5000;
    			select row_count() into v_rows;
    			select sleep(5);
    		end while;
    		end $$
    delimiter;
    
  • 如何修改大表的表結構

    1.對錶中的列的字段類型進行修改改變字段的寬度時還是會進行鎖表

    2.無法解決主從數據庫延遲的問題

    修改的方法:

    pt-online-schema-change 
    --alter="modify c varchar(150) not null default''" 
    --user=root --password=PassWord D=testDataBaseName,t=tesTableName 
    --charset=utf-8 --execute
    
  • 如何優化not in和<>查詢

    #原始的SQL語句
    SELECT
        customer_id,
        first_name,
        last_name,
        email
    FROM
        customer
    WHERE
        customer_id NOT IN (
            SELECT
                customer_id
            FROM
                payment
        )
    
    #優化後的SQL語句
        SELECT
            a.customer_id,
            a,
            first_name,
            a.last_name,
            a.email
        FROM
            customer a
        LEFT JOIN payment b ON a.customer_id = b.customer_id
        WHERE
            b.customer_id IS NULL
    
  • 使用匯總表的方法進行優化

    #統計商品的評論數(若有上億條記錄,執行起來非常慢進行全表掃描)[優化前的SQL]
    select count(*) from product_comment where product_id=999;
    
    #彙總表就是提前以要統計的數據進行彙總並記錄到數據庫中以備後續的查詢使用
    
    create table product_comment_cnt(product_id int,cnt int);
    
    #統計商品的評論數[優化後的SQL]
    #查詢出每個商品截止到前一天的累計評論數+當天的評論數
    select sum(cnt) from(
        select cnt from product_comment_cnt where product_id=999
        union all
        select count(*) from product_comment where product_id=999
        and timestr>DATE(NOW())
    ) a
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章