SQL語句的優化

SQL語句的優化

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

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

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

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

慢查詢日誌介紹

  • slow_quey_log=on 啓動記錄慢查詢日誌緩存

  • slow_query_log_file 指定慢查詢日誌的存儲路徑及文件(默認狀況下保存在MySQL的數據目錄中)服務器

  • long_query_time 指定記錄慢查詢日誌sql執行的閾值(默認爲10秒,一般改成0.001秒比較合適)session

  • log_queries_not_using_indexes 是否記錄未使用索引的SQL併發

    set global sql_query_log=on;socket

    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複製代碼
相關文章
相關標籤/搜索