mMySQL學習筆記(4)——查詢優化(筆記)

分析:
1、至少跑一天,看看生產慢SQL情況
2、開啓慢查詢日誌,設置閾值,比如超過5秒的就是慢SQL,並將它抓取出來。
3、explain+慢SQL分析
4、show profile(分析效果與explain差不多)
5、運維經理 or DBA 進行SQL數據庫服務器的參數調優。


小表驅動大表
即小的數據集驅動大的數據集


in:

select * from A where id in (select id from B)

這個語句,等價於先查詢B表,再查詢A表。小表應該是B,大表應該是A。B驅動A


exists:

select * from A where exists(select 1 from B where B.id = A.id)

現從A做一個查詢,之後在查詢B表。
意思是查看A表記錄是否與B表滿足 B.id = A.id,如果是,則返回一條記錄;如果否,則不返回。
這個語句,先查詢A,再查詢B。因此,A應該是小表。B應該是大表。 A驅動B

select ... from table where exists(subquery)

該語句理解爲:將主查詢的數據,放到子查詢中做條件驗證,根據驗證結果(true 或者Flase)來決定主查詢的數據結果是否予以保留。


hint:
1、exists(subquery)只返回true 或者Flase,因此子查詢中select 1 或者select 沒有區別。
2、exists子查詢實際上經過了優化而不是我們理解的逐條對比。
3、exists子查詢往往也可以用條件表達式、其他子查詢後者join來代替,何種最優需要具問具分。

order by(討論會不會產生filesort):
order by儘量使用index而不是filesort排序。



建表:
age ,birth兩個字段。 建立索引age,birth


1、

select * from tbla where age > 20 order by age;

以前關注用沒用索引,現在關注order by後面會不會產生filesort。這句話不會差生filesort。

2、

select * from tbla where age > 20 order by age,birth;order by age,birth

用到了索引排序,因此不會產生filesort

3、

select * from tbla where age > 20 order by birth;

產生filesort,order by後面缺少帶頭大哥,因爲age所引用到了但是是一個範圍,因此不能“從一樓到二樓”。

4、

select * from tbla where age > 20 order by birth,age;

產生filesort,這就不像where後面的and,順序反了可以優化,order by後面順序反了不會優化。

5、

select * from tbla  order by birth;

產生filesort,帶頭大哥沒有

6、

select * from tbla where birth > '2020-04-23 00:00:00' order by birth;

產生filesort,帶頭大哥沒有

7、

select * from tbla where birth > '2020-04-23 00:00:00' order by age;

不會產生filesort,order by後面用到了索引排序。

8、

select * from tbla order by age ASC,birth DESC;

帶頭大哥在,順序和索引相同。但仍會產生filesort,要麼同升,要麼同降。


總結:
MySQL支持兩種方式排序:index和filesort。index效率要高。

order by滿足下列兩個情況會用到索引排序:
1、order by後面的語句使用索引最左前綴匹配原則。
2、使用where子句與order by子句條件列組合滿足最左前綴匹配原則

如果用不到index排序,filesort排序會有兩種算法:
雙路排序:MySQL4.1之前使用雙路排序,兩次掃描磁盤得到數據。
單路排序:從磁盤讀取所有列,按照order by列在buffer對他們進行排序,然後掃描排序後的列表進行輸出,它的效率更快些,避免了第二次讀取數據。並且把隨機IO變成順序IO。

單路總體而言好於雙路。

提高order by速度:
1、order by時候儘量不要用select *。
2、嘗試提高sort_buffer_size
3、嘗試提高max_length_for_sort_data

group by:
group by實質是線排序後分組,遵循索引建立的最左前綴原則
where 優先於HAVING。能寫where就不要用HAVING。

慢查詢日誌:
MySQL慢查詢日誌是MySQL提供的一種日誌記錄,它用來記錄在MySQL中響應時間超過閾值的語句,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日誌中。

long_query_time默認10秒。

默認MySQL沒有開啓慢查詢日誌。沒事不要開啓,開啓會影響性能。

查看開啓:SHOW VARIABLES LIKE ‘%slow_query_log%’
開啓:
set global slow_query_log = 1 (僅對當前數據庫有效,重啓失效)
查看閾值:
SHOW global VARIABLES LIKE ‘%long_query_time%’
設置閾值:
set global long_query_time = 10(表示10秒)
可使用select sleep(6)驗證。

mysqldumpslow:官方提供的日誌分析工具
得到返回記錄最多的10個SQL:
mysqldumpslow -s r -t 10 /var/lib/mysql/XXXXX-slow.log

得到訪問次數最多的10個SQL:mysqldumpslow -s c -t 10 /var/lib/mysql/XXXXX-slow.log

得到按時間排序前10條裏面含有左連接的查詢語句:mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/XXXXX-slow.log

存儲過程:

DELIMITER $$

意思是將結束符改爲$$。



show profile:

MySQL調優思路:
step1:察覺到系統變慢
step2:給SQL跑一圈(重現故障)利用慢查詢日誌
step3:採取explain分析具體SQL
step4:第三部沒擺平,使用show profile

show profile是MySQL提供可以用來分析當前會話中語句執行的資源消耗情況。可以用於SQL調優的測量。

默認情況下是關閉狀態。

查看show profile是否開啓:
show variables like ‘profiling’;

開啓show profile:
set profiling=on;
開啓之後,show profile會在後臺自動抓取查詢信息。

查看所有執行過的SQL。
show profiles;

診斷SQL:
show profile cpu,block io for query Query_ID(show profiles裏面的id);
這句話診斷出一條SQL生命週期裏發生的所有事件以及發生的時長。
上述SQL中,cpu和block IO是可替換項:可添加一個或者多個,中間用逗號隔開。

type:
| ALL --顯示所有的開銷信息
| BLOCK IO --顯示塊IO相關開銷
| CONTEXT SWITCHES --上下文切換相關開銷
| CPU --顯示CPU相關開銷信息
| IPC --顯示發送和接收相關開銷信息
| MEMORY --顯示內存相關開銷信息
| PAGE FAULTS --顯示頁面錯誤相關開銷信息
| SOURCE --顯示和Source_function,Source_file,Source_line相關的開銷信息
| SWAPS --顯示交換次數相關開銷的信息

在status狀態下出現以下item說明有問題:
1、converting HEAP to MyISAM 查詢結果太大了,內存都不夠用往磁盤上搬了
2、creating tmp table 創建臨時表(拷貝數據到臨時表,用完再刪除)
3、copying to tmp table on disk 把內存中臨時表複製到磁盤,危險!!
4、locked




MySQL鎖:
對數據操作類型分:(讀鎖/寫鎖)
讀鎖:針對同一份數據,多個讀操作可以同時進行而不會互相影響。
寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。

對鎖的粒度分:
表鎖:
開銷小、加鎖快,無死鎖,鎖的粒度大,發生鎖衝突的概率最高,併發度最低。

建表:

create table mylock(
     id int primary key auto_increment,
     name varchar(20),
      ) engine myisam;

插入數據:

insert into mylock(name) values ('a'),('b'),('c'),('d'),('e');

開兩個終端(鏈接數據庫):
手動增加表鎖:
lock table 表名字 read/write , 表名字2 read/write , 其他;

解鎖:
unlock tables;(所有表全解鎖了)

查看錶是否加過鎖:
show open tables;
database tables in_use name_locked
是否加鎖0/1

如何分析表鎖定:
show status like ‘table%’;
table_locks_immediate:產生表級鎖定次數,表示可以立即獲取鎖的查詢次數,每立即獲得鎖值加1。
table_locks_waited:出現表級鎖定爭用而發生的等待次數,此值高說明存在着較爲嚴重的表級鎖爭用情況。

MyISAM讀寫鎖調度是寫鎖優先(即讀鎖寫鎖競爭時,寫鎖能搶到),由於寫鎖特性,寫鎖優先會導致其他進程讀寫永遠阻塞。

讀鎖:
session1對錶mylock加讀鎖。
session1可以讀mylock,不能寫mylock,不能讀寫其他未鎖定的表。
session2可以讀mylock,寫mylock時會進入阻塞狀態,直到session釋放鎖才執行寫操作。可以讀寫其他未鎖定的表。

寫鎖:
session1對錶mylock加寫鎖。
session1可以讀寫mylock,不能讀寫其他未加鎖的表
session2可以讀寫其他未加鎖的表,讀寫mylock會阻塞,等待session釋放鎖。

簡而言之(針對session2):讀鎖會阻塞寫,但是不會阻塞讀。而寫鎖則會把讀和寫都阻塞。

行鎖(innodb默認加鎖?):
開銷大、加鎖慢、會出現死鎖;鎖粒度最小,發生鎖衝突概率最低,併發度也高。InnoDB和MyISAM區別:1、InnoDB支持事務。2、InnoDB採用了行級鎖。

課堂實驗:
建表:

create table test_innodb_lock(a int(11) , b varchar(16))engine = innodb;

insert into test_innodb_lock values(1,'b2'),(3,'3'),(4,'4000'),(5,'5000'),(6,'6000'),(7,'7000'),(8,'8000'),(9,'9000'),(1,'b1');

建索引:

create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);

設置自動提交爲0;

set autocommit = 0;

在session1中更改數據(未提交),session1自己看到的數據更改了,但是session2看得的數據是原來的數據。
兩個session更改同一行數據:
在這裏插入圖片描述兩個session更改不同行數據:

可自由更改無影響。

索引建立之後使用不當導致行鎖升級爲表鎖

case:
在session1中執行下列語句:

update test_innodb_lock set a = 10 where b = 4000;

由於b是varchar而where後面沒有加單引號,因此導致索引失效。此時,行鎖變表鎖。seesion2中再去寫會被阻塞。

間隙鎖:
當我們用範圍條件而不是相等條件檢索數據時,會給這個範圍內的所有鍵值加鎖。
例如:
表如圖所示:
在這裏插入圖片描述
session1執行:
update test_innodb_lock set b = ‘0987’ where a >1 and a<6;
此時,未提交,MySQL會將a>1 ,<6的鍵值全上鎖。(即使沒有a=2的記錄)。

seesion2執行:
insert into test_innodb_lock values(2,‘2000’);
commit;
由於seesion1沒有commit,所以seesion2執行此句後會阻塞,直到seesion1 commit之後釋放鎖。

如何鎖定一行:
seesion1執行:
select * from test_innodb_lock where a = 8 for update;
手工爲a=8這條記錄上鎖。

session2更改a=8這行數據會阻塞,直到session1執行commit。

行鎖問題排查:
show status like ‘innodb_row_lock%’;
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

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