mysql回表慢sql SQL慢查詢常用優化方法

2021-10-20 23:03:50 字數 3308 閱讀 4001

先建立測試表並使用儲存過程插入測試資料

create table `cif_student` (

`id` varchar(40) not null,

`name` varchar(40) default null,

`age` varchar(40) default null,

`class_id` varchar(40) default null,

`class_name` varchar(40) default null,

primary key (`id`)

) engine=innodb default charset=utf8;

create table `cif_class` (

`id` varchar(40) not null,

`class_name` varchar(40) default null,

primary key (`id`)

) engine=innodb default charset=utf8;

drop procedure if exists proc_initdata;-- 如果存在此儲存過程則刪掉

create procedure proc_initdata() -- 建立儲存過程

begin

declare i int default 1;

declare j int ;

while i<=15000000 do

set j = round(rand()*15000000);

insert into cif_student(id,name,age,class_id,class_name) values(i,concat('sname',i),15,j,concat('cname',j));

insert into cif_class(id,class_name) values(i,concat('cname',i));

set i = i+1;

end while;

end;

call proc_initdata();-- 呼叫儲存過程

索引原理傳送門:了解了其原理後再來聊下具體規則就很容易明白了。

一、避免回表查詢

innodb採用聚集索引的方式。表資料檔案本身就是按 b+tree 組織的乙個索引結構,索引的 key 是資料表的主鍵,樹的葉子節點data域儲存了完整的資料記錄。稱為主索引。innodb的輔助索引 data 域則儲存相應記錄主鍵的值。

所以按照主鍵搜尋十分高效,但是按照輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄,這就是所謂回表查詢。

避免回表查詢,可以適當使用聯合索引,將被查詢的列加入索引中:

比如,若給student表的name列加上索引,使用如下查詢:

select t.id,t.name,t.class_id from cif_student t where t.name like 'sname1%';

會產生回表查詢,因為class_id是name輔助索引列裡沒有的,必須獲取到輔助索引記錄的主鍵值,然後根據主鍵值回主索引查詢。耗時約1.8s。

若給name,class_id加上聯合索引,再執行上述sql語句則不需要回表查詢。耗時約0.5s,效率顯著提公升。

二、聯合索引最左匹配原則

mysql會一直向右匹配直到遇到範圍查詢就停止匹配。如果建立(a,b,c,d)的聯合索引,a = 1 and b=2 and c>3 and d=4,這裡d是用不到索引的,如果建立(a,b,d,c)的聯合索引則可以用到。a,b,c,d的查詢順序可以任意調整,mysql查詢優化器會幫你優化成索引可以識別的形式。所以考慮建立聯合索引的時候應該重點考慮聯合索引的字段順序,比如若想建立a,b欄位聯合索引,且b欄位已有索引經常單獨查詢,則應建立(b,a)的聯合索引。

三、避免索引失效

索引列參與計算

如concat(name,'.name')='xiaoming.name'就不能使用索引。因為b+樹中存放的都是資料表中的字段值,進行檢索需要把所有記錄按照函式計算才能進行比較,成本太大。

like關鍵字

在使用like關鍵字進行查詢的查詢語句中,如果匹配字串的第乙個字元為「%」,索引不會起作用。只有「%」不在第乙個位置索引才會起作用。

使用not,<>,!=,is not null。

不等於操作符是永遠不會用到索引的,因此對它的處理只會產生全表掃瞄。 優化方法: key<>0 改為 key>0 or key<0。

tips:is null是可以使用到索引的,is not null才不能使用到索引。null值列不參與計算,<>符號和count函式會自動過濾為null的列,比如:where a != '1',則不會查詢出a為null的列。

or語句前後沒有同時使用索引。

當or語句查詢字段只有乙個是索引,該索引失效,只有當or語句左右查詢欄位均為索引時,才會生效

資料型別出現隱式轉化。

如varchar不加單引號的話可能會自動轉換為int型,使索引無效,產生全表掃瞄。

四、使用執行計畫優化

先看乙個正常的連線查詢兩張表並排序取前10條,sql如下:

select * from cif_student p left join cif_class t on p.class_name=t.class_name order by p.class_name limit 10;

發現其效率及其低下(根本無法查出)。檢視執行計畫發現驅動表p和被驅動表t都使用了全表掃瞄(type列為all,掃瞄行數rows為全表記錄),且p表使用了臨時表和非索引排序(extral列中using temporary; using filesort)。

優化方式一

首先想到的是連線查詢,驅動表和被驅動表存在連線條件,則被驅動表連線字段需要加上索引。

給t表加上索引後執行,發現效率大為提公升,耗時約0.5s。執行計畫如下:

發現t表type為ref,即非主鍵非唯一索引等值掃瞄,但是p表排序還是未使用到索引,再給p表class_name欄位加上索引後執行,耗時約0.005s,執行計畫如下:

優化方式二

mysql連線查詢執行順序為先執行連線查詢,再執行where條件,再執行排序和limit擷取。

表p和表t各10多萬條資料,執行連線查詢就是億級別資料,再排序時肯定效率就非常低了。可以使用子查詢先縮小p表查詢範圍,再做連線。優化後sql如下:

select * from (select * from cif_student pp order by pp.class_name limit 10) p left join cif_class t on p.class_name=t.class_name;

發現不加索引,其耗時也僅為2s左右。兩表加上索引後耗時約0.008秒,與方式一執行效率相當。

mysql所有表都查詢特別慢 MySQL慢查詢日誌

慢查詢日誌是mysql日誌功能的一種,記錄查詢時間大於預設或設定值的sql語句,也就是記錄執行時間超過long query time值的sql 支援寫入檔案中 mysql資料庫的long query time預設值的10,即10秒。這個值可由我們自己設定。預設情況下,mysql資料庫沒有開啟慢查詢日...

mysql 慢查詢 MySQL慢查詢

一 簡介 開啟慢查詢日誌,可以讓mysql記錄下查詢超過指定時間的語句,通過定位分析效能的瓶頸,才能更好的優化資料庫系統的效能。二 引數說明 slow query log 慢查詢開啟狀態 slow query log file 慢查詢日誌存放的位置 這個目錄需要mysql的執行帳號的可寫許可權,一般...

mysql常見慢sql mysql 慢SQL分析

開啟慢sql記錄 為什麼要開啟慢sql記錄 mysql在執行過程中,某些sql可能會執行較長時間,我們通過配置一些東西,把這些sql記錄下來,以便我們分析和優化sql。首先先檢視是否已經開啟了記錄設定 mysql show variables like query variable name val...