1. 程式人生 > 實用技巧 >分析sql執行效率1

分析sql執行效率1

1.檢視是否開啟慢查詢日誌
show variables like "%slow_query_log%";

2.檢視慢查詢閾值

show global variables like "long_query_time";

set global long_query_time = 1;

3.確定慢查詢日誌路徑

show global variables like "datadir";

4.確定慢查詢日誌的檔名

show global variables like "slow_query_log_file";

5.有時慢查詢正在執行,已經導致資料庫負載偏高了,而由於慢查詢還沒執行完,因此慢查詢日誌還看不到任何語句。此時可以使用 show processlist 命令判斷正在執行的慢查詢。show processlist 顯示哪些執行緒正在執行。如果有 PROCESS 許可權,則可以看到所有執行緒。否則,只能看到當前會話的執行緒

show processlist

知識擴充套件:如果不使用 FULL 關鍵字,在 info 欄位中只顯示每個語句的前 100 個字元,如果想看語句的全部內容可以使用 full 修飾(show full processlist)

Time:表示執行時間

Info:表示 SQL 語句

我們這裡可以通過它的執行時間(Time)來判斷是否是慢 SQL

6.實戰

CREATE TABLE `t1` ( /* 建立表t1 */
`id` int(11) NOT NULL auto_increment,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,

`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '記錄建立時間',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '記錄更新時間',
PRIMARY KEY (`id`),
KEY `idx_a` (`a`),
KEY `idx_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

drop procedure if exists insert_t1; /* 如果存在儲存過程insert_t1,則刪除 */

delimiter ;;
create procedure insert_t1() /* 建立儲存過程insert_t1 */
begin
declare i int; /* 宣告變數i */
set i=1; /* 設定i的初始值為1 */
while(i<=1000)do /* 對滿足i<=1000的值進行while迴圈 */
insert into t1(a,b) values(i, i); /* 寫入表t1中a、b兩個欄位,值都為i當前的值 */
set i=i+1; /* 將i加1 */
end while;
end;;
delimiter ; /* 建立批量寫入1000條資料到表t1的儲存過程insert_t1 */
call insert_t1(); /* 執行儲存過程insert_t1 */

drop table if exists t2; /* 如果表t2存在則刪除表t2 */
create table t2 like t1; /* 建立表t2,表結構與t1一致 */
insert into t2 select * from t1; /* 將表t1的資料匯入到t2 */

執行查詢:

explain select * from t1 where b=100;

Explain 的結果各欄位解釋如下:

加粗的列為需要重點關注的項。

列名解釋
id 查詢編號
select_type 查詢型別:顯示本行是簡單還是複雜查詢
table 涉及到的表
partitions 匹配的分割槽:查詢將匹配記錄所在的分割槽。僅當使用 partition 關鍵字時才顯示該列。對於非分割槽表,該值為 NULL。
type 本次查詢的表連線型別
possible_keys 可能選擇的索引
key 實際選擇的索引
key_len 被選擇的索引長度:一般用於判斷聯合索引有多少列被選擇了
ref 與索引比較的列
rows 預計需要掃描的行數,對 InnoDB 來說,這個值是估值,並不一定準確
filtered 按條件篩選的行的百分比
Extra 附加資訊
表 1-explain 各欄位解釋

其中 explain 各列都有各種不同的值,這裡介紹幾個比較重要列常包含的值:包含 select_typ、type 和 Extra。

下面將列出它們常見的一些值,可稍微過一遍,不需要完全記下來,在後續章節分析 SQL 時,可以返回查詢本節內容並對比各種值的區別。

2.1 select_type

select_type 的值解釋
SIMPLE 簡單查詢 (不使用關聯查詢或子查詢)
PRIMARY 如果包含關聯查詢或者子查詢,則最外層的查詢部分標記為 primary
UNION 聯合查詢中第二個及後面的查詢
DEPENDENT UNION 滿足依賴外部的關聯查詢中第二個及以後的查詢
UNION RESULT 聯合查詢的結果
SUBQUERY 子查詢中的第一個查詢
DEPENDENT SUBQUERY 子查詢中的第一個查詢,並且依賴外部查詢
DERIVED 用到派生表的查詢
MATERIALIZED 被物化的子查詢
UNCACHEABLE SUBQUERY 一個子查詢的結果不能被快取,必須重新評估外層查詢的每一行
UNCACHEABLE UNION 關聯查詢第二個或後面的語句屬於不可快取的子查詢
表 2-select_type 各項值解釋

2.2 type

type 的值解釋
system 查詢物件表只有一行資料,且只能用於 MyISAM 和 Memory 引擎的表,這是最好的情況
const 基於主鍵或唯一索引查詢,最多返回一條結果
eq_ref 表連線時基於主鍵或非 NULL 的唯一索引完成掃描
ref 基於普通索引的等值查詢,或者表間等值連線
fulltext 全文檢索
ref_or_null 表連線型別是 ref,但進行掃描的索引列中可能包含 NULL 值
index_merge 利用多個索引
unique_subquery 子查詢中使用唯一索引
index_subquery 子查詢中使用普通索引
range 利用索引進行範圍查詢
index 全索引掃描
ALL 全表掃描
表 3-type 各項值解釋

上表的這些情況,查詢效能從上到下依次是最好到最差。

2.3 Extra

Extra 常見的值解釋例子
Using filesort 將用外部排序而不是索引排序,資料較小時從記憶體排序,否則需要在磁碟完成排序 explain select * from t1 order by create_time;
Using temporary 需要建立一個臨時表來儲存結構,通常發生對沒有索引的列進行 GROUP BY 時 explain select * from t1 group by create_time;
Using index 使用覆蓋索引 explain select a from t1 where a=111;
Using where 使用 where 語句來處理結果 explain select * from t1 where create_time=‘2019-06-18 14:38:24’;
Impossible WHERE 對 where 子句判斷的結果總是 false 而不能選擇任何資料 explain select * from t1 where 1<0;
Using join buffer (Block Nested Loop) 關聯查詢中,被驅動表的關聯欄位沒索引 explain select * from t1 straight_join t2 on (t1.create_time=t2.create_time);
Using index condition 先條件過濾索引,再查資料 explain select * from t1 where a >900 and a like “%9”;
Select tables optimized away 使用某些聚合函式(比如 max、min)來訪問存在索引的某個欄位是 explain select max(a) from t1;
表 4-Extra 常見值解釋及舉例