3.查詢截取分析

基於MySQL5.5


SQL優化

SQL優化步驟
  • 上線後觀察SQL,等待SQL跑一天,查看生產上慢SQL的執行情況
  • 開啓慢查詢日誌,設置閾值,比如超過5秒的就是慢SQL,將其抓取出來
  • explain+慢SQL進行分析
  • show profile查詢SQL在mysql服務器裏面的執行細節和聲明週期
  • 確定是否需要調整SQL數據庫參數,找DBA討論
優化
小表驅動大表:
  • 即小的數據集驅動大的數據集,關聯查詢時,數據量小的表先查詢
    • 關鍵字in,先執行in中的子查詢,所以當in中的數據量較小時效率較高
    • 關鍵字exists,先執行外面的SQL,所以當主表數據量較少時,使用exists更好
  • EXISTS:
    • SELECT … FROM table WHERE EXISTS(subquery),該語法可以理解爲,將主查詢的數據,放到子查詢中做條件驗證,根據驗證結果(TRUE或FALSE)來決定主查詢的數據結果是否保留
    • EXISTS(subquery)只返回TRUE或FALSE,因此子查詢中的SELECT*也可以是SELECT 1或SELECT ‘x’,實際執行時會忽略SELECT清單,因此沒有區別
    • EXISTS子查詢的實際執行過程可能經過了優化,而不是我們理解的逐條對比,如果擔憂效率問題,可進行實際檢驗以確定是否有效率問題
    • EXISTS子查詢往往也可以用條件表達式,其他子查詢或者JOIN來替代,何種最優需要具體問題具體分析
order by關鍵字優化
  • MySQL支持二種方式的排序,FileSort和Index相比,Index效率高,它指MySQL掃描索引本身完成排序,FileSort方式效率較低;儘可能在索引列上完成排序操作,遵照索引建的最佳做前列,否則mysql將使用filesort
  • Order By滿足兩種情況,會使用Index方式排序:
    • ORDER BY語句使用索引最左前列
    • 使用Where子句與Order By子句條件列組合滿足最左前列
  • FileSort有兩中算法:
    • 雙路排序
      • MySQL4.1之前是使用雙路排序,字面意思就是兩次掃描磁盤,最終得到數據,讀取行指針和order by列,對他們進行排序,然後掃描已經排序好的列表,按照列表中的值重新從列表中取值;
      • 從磁盤取排序字段,在buffer進行排序,再從磁盤取其他字段
    • 單路排序
      • 從磁盤讀取查詢需要的所有列,按照order by列在buffer對它們進行排序,然後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取數據;並且把隨機IO變成了順序IO,但是它會使用更多的空間,因爲它把每一行都保存在內存中了。
      • 問題:在sort_buffer中,方法B比方法A要多佔用很多空間,因爲方法B是把所有字段都取出,所以有可能取出的數據的總大小超出了sort_buffer的容量,導致每次只能取sort_buffer容量大小的數據,進行排序(創建tmp,多路合併),排完後再取sort_buffer容量大小,再排,從而多次IO;本來想着節省IO次數,結果導致了大量的IO操作,反而得不償失。
  • 提高order by的速度,優化策略:
    • 增大sort_buffer_size參數的設置:不管用哪種算法,提高這個參數都會提高效率,當然,要根據系統的能力去提高,因爲這個參數是針對每個進行的
    • 增大max_length_for_sort_data參數的設置:提高這個參數,會增加用改進算法的頻率,但是如果設得太高,數據總容量超出sort_buffer_size的概率就增大,明顯症狀是高的磁盤IO活動和低的處理器使用率
    • Order by時select *是一個大忌,只Query需要的字段,這點非常重要
      • 當Query的字段大小總和小於max_length_for_sort_data而排序字段不是TEXT|BLOB類型時,會用改進後的算法-單路排序,否則用老算法-多路排序
      • 兩種算法的數據都有可能超出sort_buffer的容量,超出之後,會創建tmp文件進行合併排序,導致多次IO,但是用單路排序算法的風險會更大一些,所以要提高sort_buffer_size
group by關鍵字優化
  • group by實質是先排序後進行分組,遵照索引建立的最佳左前綴進行
  • 當無法使用索引列時,增大max_length_for_sort_data參數的設置+增大sort_buffer_size參數的設置
  • where高於having,能寫在where限定的條件就不要去having限定了

慢查詢日誌

  • MySQL的慢查詢日誌是MySQL提供的一種日誌記錄,它用來記錄在MySQL中響應時間超過閾值的語句,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日誌中
  • long_query_time的默認值爲10,意思是運行10秒以上的語句
  • 由他來查看哪些SQL超出了我們的最大忍耐時間值,比如一條SQL執行超過5秒鐘,我們就算慢SQL,希望能收集超過5秒的SQL,結合之前的explain進行全面的分析
說明
  • 默認情況下,MySQL數據庫沒有開啓慢查詢日誌,需要我們手動來設置這個參數
  • 當然,如果不是調優需要的話,一般不建議啓動該參數,因爲開啓慢查詢日誌會或多或少的帶來一定的性能影響,慢查詢日誌支持將日誌記錄寫入文件
查看是否開啓及如何開啓
  • 默認:SHOW VARIABLES LIKE ‘%slow_query_log%’;
  • 開啓:set global slow_query_log = 1;
  • 使用以上命令開啓慢查詢日誌,只能對當前數據庫生效,如果MySQL重啓後則不失效
  • 如果要永久生效,需要修改my.cnf,[mysqld]下增加或修改參數, slow_query_log和slow_query_log_file後,然後重啓MySQL服務器,也即將如下兩行配置進my.cnf(windows爲my.ini)文件,
slow_query_log=1
slow_query_log_file=/var/lib/mysql/CADE-slow.log
  • 關於慢查詢的參數slow_query_log_file,它指定慢查詢日誌文件的存放路徑,系統默認會給一個缺省的文件host_name-slow.log(如果沒有指定參數slow_query_log_file的話)
  • 這個是由參數long_query_time控制,默認情況下long_query_time的值爲10秒,命令:SHOW VARIABLES LIKE ‘long_query_time%’;
  • 可以通過命令修改set global long_query_time=3,或者在my.cnf(my.ini)參數裏進行修改
long_query_time=3
log_output=FILE
  • 假如運行時間正好等於long_query_time的情況,並不會被記錄,也就是說,在mysql的源碼裏是判斷大於long_query_time,而非大於等於
  • 查詢當前系統中有多少條慢查詢記錄:SHOW GLOBAL STATUS LIKE ‘%SLOW_QUERIES%’;
查看mysqldumpslow的幫助信息
  • s:是表示按照何種方式排序
  • c:訪問次數
  • l:鎖定時間
  • r:返回記錄
  • t:查詢時間
  • al:平均鎖定時間
  • ar:平均返回記錄數
  • at:平局查詢時間
  • t:即爲返回前面多少條的數據
  • g:後邊搭配一個正則匹配模式,大小寫不敏感的
  • 得到返回記錄集最多的10個SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/CADE-slow.log
  • 得到訪問次數最多的10個SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/CADE-slow.log
  • 得到按照時間排序的前10條裏面含有左連接的查詢語句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/CADE-slow.log
  • 另外建議在使用這些命令時結合|和more使用,否則有可能出現爆屏的情況
mysqldumpslow -s r -t 10 /var/lib/mysql/CADE-slow.log | more

數據腳本

  • 創建函數,假如報錯:This funtion has none of DETERMINISTIC… 由於開啓了慢查詢日誌,因爲我們開啓了bin-log,我們就必須爲我們的function指定一個參數
show variables like 'log_bin_trust_function_creators';
set global log_bin_trust_function_creators=1;
  • 這樣添加了以後,如果mysqld重啓,上述參數又會消失,永久方法,my.cnf(my.ini)加上log_bin_trust_function_creators=1
隨機產生字符串
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
	DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
	DECLARE return_str VARCHAR(255) DEFAULT '';
	DECLARE i INT DEFAULT 0;
	WHILE i < n DO
	SET return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
	SET i = i + 1;
	END WHILE;
	RETURN return_str;
END $$
產生隨機數
DELIMITER $$
CREATE FUNCTION rand_num() RETURNS INT(5)
BEGIN
	DECLARE i INT DEFAULT 0;
	SET i = FLOOR(100+RNAD()*10);
	RETURN i;
END $$
向某表裏插入數據
DELIMITER $$
 CREATE PROCEDURE insert_student(IN START INT(10),IN max_num INT(10))
BEGIN
	DECLARE i INT DEFAULT 0;
	SET autocommit = 0;
	REPEAT
		SET i = i + 1;
		INSERT INTO student(id,name,age,school) VALUES ((START+i),rand_string(5),rand_string(3),rand_string(10));
		UNTIL i = max_num
	END REPEAT;
	COMMIT;
END $$
DELIMITER ;

SHOW PROFILE

  • 是什麼:是mysql提供的可以用來分析當前會話中語句執行的資源消耗情況,可以用於SQL的調優的測量
  • 官網:http://dev.mysql.com/doc/refman/5.5/en/show-profile.html
  • 默認情況下,該參數處於關閉狀態,並保存最近15次運行結果
SHOW VARIABLES LIKE 'profiling';
SET profilint=on;
show variables like '%profil%';
分析步驟
  • 是否支持:看看當前的mysql版本是否支持
  • 開啓功能:默認是關閉的,使用前需要開啓
  • 運行SQL
    • SELECT * FROM EMP group by id%10 limit 1500;
    • SELECT * FROM emp group by id%20 order by 5;
  • 診斷SQL:
    • show profiles;
    • SHOW PROFILE cpu,block io for query #id#;
  • 日常開發需要注意的結論
    • converting HEAP to MyISAM查詢結果太大,內存都不夠用了往磁盤上搬了
    • Creating tmp table創建臨時表,拷貝數據到臨時表,用完再刪除
    • Copying to tmp table on dist把內存中臨時表複製到磁盤,危險!
  • type(status):
    • ALL:顯示所有的開銷信息
    • BLOCK IO:顯示塊IO相關開銷
    • CONTEXT SWITCHES:上下文切換相關開銷
    • CPU:顯示CPU相關開銷信息
    • IPC:顯示發送和接收相關開銷信息
    • MEMORY:顯示內存相關開銷信息
    • PAGE FAULTS:顯示頁面錯誤相關開銷信息
    • SOURCE:顯示和Source_function,Source_file,Source_line相關的開銷信息
    • SWAPS:顯示交換次數相關開銷的信息

全局查詢日誌

  • 配置啓用
    • 在my.cnf中設置如下:開啓general_log=1
    • 記錄日誌文件的路徑:general_log_file=/path/logfile
    • 輸出格式:log_output=FILE
  • 編碼啓用
    • set global general_log=1;
    • set global log_output=‘TABLE’;
    • 此後,你所編寫的所有SQL語句,都將會記錄到mysql庫裏的general_log表中,可以使用命令查看:select * from mysql.general_log
  • 永遠不要在生產環境開啓這個功能

附錄

在這裏插入圖片描述

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