1. 程式人生 > >Mysql優化之高階

Mysql優化之高階

一、exists和in - - - 小表驅動大表

前言: 500*10000和10000*500,在數學角度來說是沒什麼區別的,從java角度來說是這樣的:

for(int i=0;i<500;i++){
    for(int j;j<10000;j++){
    }
}

for(int i=0;i<10000;i++){
    for(int j;j<500;j++){
    }
}

但是對於mysql來說是有區別的。這關係到連線和釋放的次數。

簡介: exists和in到底誰效能好是沒有絕對一說的,這需要看錶資料的大小。

in: SELECT * FROM dept where deptno in (SELECT deptno FROM emp )

這句sql,在mysql翻譯之後是這樣的:

for(SELECT deptno FROM emp) {
    forSELECT * FROM dept where emp.deptno =dept.deptno){

    }
}

exists: SELECT * FROM dept where EXISTS (SELECT 1 FROM emp where emp.deptno =dept.deptno)這句sql,在mysql翻譯之後是這樣的(這裡的1可以換成任意常量,比如A、a之類的):

for(SELECT deptno FROM dept ) {
    forSELECT
* FROM dept where emp.deptno =dept.deptno){ } }

分析: 如果使用in,則需要連線emp的條數次;如果使用exists,則需要連線dept 的條數次。

案例:向emp中插入100萬條記錄,向dept表中插入5萬條記錄。
這裡寫圖片描述
這裡寫圖片描述

結論: emp表比dept表小的時候使用in,emp表比dept表大的時候使用exists。即:內小in,外小exists。

二、order by 之 filesort

簡介: order by的排序總共有兩種,一種是使用索引(index)進行排序,還有一種是沒法使用索引進行排序的,稱為filesort排序(檔案排序)。using index效率比using filesort高。

準備工作:

CREATE TABLE test01 (
c1  varchar(3)  ,
c2  varchar(3),
c3  varchar(3),
c4 varchar(3)
);
create index  c1c2c3 on test01(c1,c2,c3);
insert into test01 values('a1','a2','a3','a4');
insert into test01 values('b1','b2','b3','b4');

幾種常見的場景:

1、只有order by 沒有where條件:這個可能其他版本的mysql是不會產生filesort的(因為我看到網上說這種情況是不會產生的),我也沒試過,但是mariadb的10.0.17版本是會產生的
這裡寫圖片描述
這裡寫圖片描述
2、where條件中有c1,但是order by中沒有c1,只有c2和c3
這裡寫圖片描述
3、where 條件中有c1 ,但是order by中只有c3: 這個就屬於中間兄弟丟了
這裡寫圖片描述
4、where條件中有c1,但是order by中沒有c1,只有c2和c3,而且c2正序,c3倒序:必須同正同逆
這裡寫圖片描述
5、where條件中有c1,order by中有c1、c2和c3,但是order by c1 ,c3,c2:必須要和複合索引欄位順序一致
這裡寫圖片描述
6、where 條件中有c1 ,但是c1的條件是個範圍,order by中只有c2和c3: 範圍之後全失效
這裡寫圖片描述
7、where 條件中有c1 ,但是c1的條件是個範圍,order by中有c1、c2和c3:
這裡寫圖片描述
8、where條件中沒有c1,有c2,但是order by中有c1:這個可能不同版本之間也有不同的答案
這裡寫圖片描述
9、where條件中沒有c1,但是order by中也沒有c1,只有c2和c3: 這個就屬於帶頭大哥死了這裡寫圖片描述

總結: 產生filesort的原因主要有:

1、帶頭大哥或者中間兄弟丟了。

2、order by之後的欄位順序和複合索引中的欄位順序不一致。

3、沒有where條件(不同版本的mysql可能不一樣)

4、尚矽谷總結圖:
這裡寫圖片描述

filesort的兩種演算法:雙路排序和單路排序

雙路排序: mysql在4.1之前使用的都是雙路排序,字面的意思就是兩次掃描磁碟,最終得到資料。即:讀取行指標和order by 列,對他們進行排序,然後掃面已經排序好的列表,按照列表中的值重新從列表中讀取對應的資料輸出。簡單來說就是:從磁碟中讀取需要排序的欄位,在buffer中進行排序,然後再從磁碟中讀取其他欄位。

單路排序: 雙路排序需要讀取兩次磁碟,但是i/o是非常消耗記憶體和時間的,所以,能不能將讀取磁碟的時間減少為1次呢?於是就有了單路排序:從磁碟中讀取查詢需要的所有列,按照order by列在buffer中對他們進行排序,然後掃描排序之後的列表進行輸出,他的效率要比雙路快,因為他將每一行的所有欄位都儲存在了記憶體中。

單路排序的缺點: 因為單路是將所有的欄位都放在了記憶體中,假如,這張表是在太大,大的超過了sort_buffer這個值了,那麼每次都只能取sort_buffer的資料,來進行排序(會建立臨時tmp檔案,然後多路合併),相當於多路了,這樣一來效率比雙路還要差。

優化單路:

1、儘量少用select *,想要那個欄位就查那個。

2、在配置檔案中增大sort_buffer_size 引數的值。

3、適當調大max_length_for_sort_data,這個值將會決定filesort使用哪種演算法,如果查詢的欄位大小綜合小於這個值,那麼將會使用單路演算法,否則使用雙路演算法。但是如果設定的太高,因為單路演算法是查詢所有欄位,這樣就會導致單路超過sort_buffer_size的可能性增大,從而產生單路的缺點。

三、group by 之 temporary

簡介: 分組之前必排序,換句話說,有using temporary 的地方必定有using filesort。using temporary 的意思就是建立臨時檔案,using filesort的意思就是使用檔案排序,兩者都是導致你查詢速度變慢的原因。

案例:
這裡寫圖片描述

幾種常見情況:
這裡寫圖片描述
總結: 情況和order by的差不多,只是從using filesort變到了using temporary,優化方式當然是想方設法使用索引來進行排序,當然必要的時候可以修改或者重新建立一個符合需求的索引。

四、慢查詢日誌分析

檢視是否開啟: show variables like ‘%slow_query_log%’; mysql預設是關閉的,需要我們手動去開啟。
這裡寫圖片描述
開啟慢查詢:

1、本次有效:即重啟mysql之後就會失效。set global slow_query_log=1;
這裡寫圖片描述
注意:mysql中預設的慢查詢時間為10秒,但是生產環境中怎麼可能讓一條sql執行10秒鐘。。。
這裡寫圖片描述

2、永久有效:即修改配置檔案。這裡我們配置慢查詢的時間為3秒(超過三秒中的查詢都會別記錄到慢查詢日誌中)

slow_query_log=TRUE
slow_query_log_file="D:/mariaDB/mariadb-10.0.17-winx64/slow_query_log.txt"
long_query_time=3

這裡寫圖片描述
測試: 我們可以寫幾句sql測試一下。
這裡寫圖片描述
捕獲慢查詢: select sleep(4) 的意思就是睡眠4秒鐘。我們開啟慢查詢日誌檔案(slow_query_log.txt),我們可以看到超過3秒鐘的sql都被記錄了下來。
這裡寫圖片描述