🌈MySQL真的就CRUD嗎?✨來看看2k和12k之間的差距(下)

這是我參與8月更文挑戰的第12天,活動詳情查看:8月更文挑戰html

🌈往期回顧

    感謝閱讀,但願能對你有所幫助,博文如有瑕疵請在評論區留言或在主頁我的介紹中添加我私聊我,感謝每一位小夥伴不吝賜教。我是XiaoLin,既會寫bug也會唱rap的男人java

4、Explain性能分析

4.一、概述

    使用 EXPLAIN 關鍵字能夠模擬優化器執行 SQL 查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結構的性能瓶頸。使用語法是:Explatn+SQL語句,他執行後返回的信息有好幾列。python

image-20210526152321356

4.二、數據準備

create table course
(
cid int(3),
cname varchar(20),
tid int(3)
);
create table teacher
(
tid int(3),
tname varchar(20),
tcid int(3)
);

create table teacherCard
(
tcid int(3),
tcdesc varchar(200)
);


insert into course values(1,'java',1);
insert into course values(2,'html',1);
insert into course values(3,'sql',2);
insert into course values(4,'web',3);

insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);

insert into teacherCard values(1,'tzdesc') ;
insert into teacherCard values(2,'twdesc') ;
insert into teacherCard values(3,'tldesc') ;
複製代碼

4.三、id

    explain返回的結果集中的id列表示select查詢的序列號,表示查詢中執行 select 子句或操做表的順序。mysql

    id 的每一個號碼,表示一趟獨立的查詢,一個 sql 的查詢趟數越少越好。web

# 查詢課程編號爲2或者教師編號爲3的老師信息
EXPLAIN select t.* from teacher t 
left join teacherCard tc 
on tc.tcid = t.tcid
left join course c 
on c.tid = t.tid
where c.cid = 2 or t.tid = 3
複製代碼

image-20210526154029652

    咱們能夠發現,id值相同,從上往下順序執行。算法

img    那要是id值不一樣呢?sql

# 查詢教授SQL課程的老師的描述(desc)
# 咱們若是不使用子查詢的話,會發現id值仍是相同,因此下面將展現子查詢的形式
EXPLAIN select tc.tcdesc from teacherCard tc where tc.tcid = 
(select t.tcid from teacher t where  t.tid =  
	(select c.tid from course c where c.cname = 'sql')
);
複製代碼

image-20210526160427308

    id 不一樣,若是是子查詢,id 的序號會遞增,id 值越大優先級越高,越先被執行,在嵌套子查詢時,先查內層 再查外層。因此先查詢c表,而後是t表,最後是tc表。shell

img    那繼續深刻,若是相同1又有不一樣的id呢?數據庫

# 查詢教授SQL課程的老師的描述(desc)
# 咱們採用子查詢1加多表的形式進行查詢
explain select t.tname ,tc.tcdesc from teacher t,teacherCard tc where t.tcid= tc.tcid
and t.tid = (select c.tid from course c where cname = 'sql') ;
複製代碼

    id值越大越優先,若id值相同,從上往下順序執行。vim

image-20210526163447435

4.四、select_type

    select_type 表明查詢的類型,主要是用於區別普通查詢、聯合查詢、子查詢等的複雜查詢。

屬性 含義
SIMPLE 簡單的 select 查詢,查詢中不包含子查詢或者 UNION鏈接查詢
PRIMARY 查詢中若包含任何複雜的子部分,最外層查詢則被標記爲 Primary
DERIVED 使用到了臨時表
SUBQUERY 包含了子查詢SQL中的子查詢(非最外層)
DEPEDENT SUBQUERY 在SELECT或WHERE列表中包含了子查詢,子查詢基於外層
UNCACHEABLE SUBQUERY 沒法使用緩存的子查詢
UNION 若是有table1 union table2 ,則table1 就是derived,table2就是union
UNION RESULT 告知開發人員,那些表之間存在union查詢

4.4.一、SIMPLE

    簡單的 select 查詢,查詢中不包含子查詢或者 UNION鏈接查詢。

select * from teacher
複製代碼

image-20210526170025751

4.4.二、PRIMARY

    查詢中若包含任何複雜的子部分,最外層查詢則被標記爲 Primary。

4.4.三、DERIVED

    使用到了臨時表就會被標記爲DERIVED。他有兩種1狀況:

  1. 在from子查詢中只有一張表。
explain select  cr.cname  from ( select * from course where tid in (1,2) ) cr ;
複製代碼
  1. 在from子查詢中, 若是有table1 union table2 ,則table1 就是derived,table2就是union
explain select  cr.cname 	from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;
複製代碼

4.4.四、UNION

    若第二個 SELECT 出如今 UNION 以後,則被標記爲 UNION,第一個SELECT會被標記爲DERIVED

explain select  cr.cname 	from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;
複製代碼

image-20210526165729964

4.4.五、UNION RESULT

    告知開發人員,那些表之間存在union查詢

explain select  cr.cname 	from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;
複製代碼

image-20210526165837982

4.五、table

    table代表這個數據是基於哪張表的。

# 給一條複雜一點的SQL
explain select  cr.cname 	from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;
複製代碼

image-20210526165108816

    在id = 1的查詢的table中,有一個<derived2>的值,說明是這個查詢用到了衍生表,衍生表的出處是id爲2的衍生表。

4.六、type

    type表示的是索引類型,是較爲重要的一個指標,性能從高到低依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index >ALL

    通常來講,得保證查詢至少達到 range 級別,最好能達到 ref,其中system、const是理想狀況,通常很難達到,通常達到的是ref或者range。

    若是想對type優化的前提是必須有索引。

4.6.一、system

    只有一條數據的系統表或者是衍生表中只有一條數據的主查詢,通常是沒法達到的,忽略不計。

4.6.二、const

    表示經過索引一次就找到了,僅僅能查詢到一條數據的SQL,用於Primary key 或 unique,只針對這兩個索引有效。也是很難達到的。

# 建立表
create table user
(
	tid int(3),
	tname varchar(20)
);

insert into test01 values(1,'xiaolin') ;
commit;

# 添加索引
alter table test01 add constraint tid_pk primary key(tid) ;

# 測試
explain select * from (select * from test01 )t where tid =1 ;
複製代碼

image-20210526172245760

4.6.三、eq_ref

    惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配(有且只有一個,不能多也不能爲0)。常見於主鍵或惟一索引掃描。是可遇不可求的。

explain select t.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid
複製代碼

image-20210526182358229

4.6.四、ref

    非惟一性索引掃描,對於每一個索引鍵的查詢,返回匹配的全部行。

# 先給teacher表的name字段加一個索引
alter table teacher add index index_name(tname);

# 在修改數據庫表,兩條語句用同一個name
explain SELECT * from teacher where tname = 'tw';
複製代碼

image-20210526193422920

4.6.五、range

    只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪一個索引通常就是在你的 where 語句中出現了 between<>in (有時候會失效,會轉爲無索引狀態)等的查詢這種範圍掃描索引掃描比全表掃描要好,由於它只須要開始於索引的某一點,而結束語另外一點,不用掃描所有索引。

# 給teacher的tid加一個普通索引
alter table teacher add index index_id(tid);

# 查詢id小於3的老師
explain select * from teacher where tid < 3
複製代碼

image-20210526195452461

4.6.六、index

    查詢所有加了索引的那一列數據。

# 咱們剛剛給tid加了索引
explain select tid from teacher;
複製代碼

image-20210526200118276

4.6.七、all

    查詢表中的全部數據,通常是沒有索引的狀況。tname字段是沒有索引的。

image-20210526200213992

4.6.八、總結

  • system\const:結果只有一條數據。
  • eq_ref:結果多條數據,可是每條數據是惟一的。
  • ref:結果多條,可是每條數據是0條或者多條。

4.七、possible_keys

    顯示可能應用在這張表中的索引,一個或多個。查詢涉及到的字段上若存在索引,則該索引將被列出,但不必定被查詢實際使用。

4.八、key

    實際使用的索引。若是爲NULL,則沒有使用索引。

4.九、key_len

    表示索引中使用的字節數,可經過該列計算查詢中使用的索引的長度。 key_len 字段可以幫你檢查是否充分的利用上了索引。ken_len 越長,說明索引使用的越充分。

image-20210526201202193

    key_len的計算方式:

  1. 先看索引上字段的類型+長度好比 int=4 、varchar(20) =20 、 char(20) =20。
  2. 若是是 varchar 或者 char 這種字符串字段,視字符集要乘不一樣的值,好比 utf-8要乘 3,GBK 要乘 2。
  3. archar 這種動態字符串要加 2 個字節。
  4. 容許爲空的字段要加 1 個字節。
  5. 若是是複合索引的話,key_len的長度是當前索引以及以前的索引之和。

4.十、ref

    指明當前表所參照的字段,若是可能的話,是一個常數。哪些列或常量被用於查找索引列上的值。

4.十一、rows

    rows 列顯示 MySQL 認爲它執行查詢時必須檢查的行數。越少越好!

4.十二、Extra

    其餘的額外重要的信息。

4.12.一、Using filesort

    出現這個說明你的SQL性能消耗大,須要額外的依次排序(查詢),比方說有年齡、名字字段,我先經過名字查找出來,而後再根據年齡排序。

    對於單索引來講,若是排序和查找是同一個字段,就不會出現Using filesort,反之亦然。

    對於複合索引來講,不能跨列,要知足最佳左前綴,where和order by按照複合索引順序使用,不要跨列或者無序使用。

# 咱們先刪除掉course中cid的主鍵,再執行查詢
EXPLAIN select * from course where tid=1 order by cid
複製代碼

image-20210527101047367

4.12.二、Using temporary

    使了用臨時表保存中間結果,表示性能損耗比較大。MySQL 在對查詢結果排序時使用臨時表。常見於排序order by 和分組查詢 group by,已經有了一張表,可是不使用,必須使用額外一張表來進行存儲。

    避免出現Using temporary的方法:查詢哪些列就用哪些列來分組。

4.12.三、Using index

    Using index 表明表示相應的 select 操做中使用了覆蓋索引(Covering Index),只要使用到的列所有都在索引中,就是索引覆蓋。他**避免訪問了表的數據行,性能獲得了提高!**緣由在於這條SQL查詢不讀取源文件,只從索引文件中獲取數據,不在原表中查詢(不回表查詢)。

  1. 若是同時出現 using where,代表索引被用來執行索引鍵值的查找。

  2. 若是沒有同時出現 using where,代表索引只是用來讀取數據而非利用索引執行查找。

  3. 若是用到了覆蓋索引的時候,會對possible_keys和key形成影響:

    • 若是沒有where,則索引只出如今key中。
    • 若是有where,則索引會出如今possible_keys和key中

4.12.四、Using where

    代表使用了 where 過濾(既須要從索引中去查,又須要回原表中查詢)。

# 索引列id能夠從索引中查詢,可是除了id以外的其餘列須要去原表中查詢 
explain SELECT * from course c where c.tid =1
複製代碼

image-20210527115242995

4.12.五、Using join buffer

    代表使用了鏈接緩存。

explain SELECT * from course c,teacher t where t.tid = c.tid
複製代碼

image-20210527104617003

5、單表SQL優化

    建表語句:

CREATE TABLE `dept` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`deptName` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(40) DEFAULT NULL,
ceo INT NULL ,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `emp` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`empno` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`deptId` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
#CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
複製代碼

5.一、全值索引我最愛

    全值索引我最愛指的是,查詢的字段按照順序在索引中均可以匹配到!咱們要根據聯合索引字段的順序,不能出現跨列的現象。

    SQL 中查詢字段的順序,跟使用索引中字段的順序,沒有關係。優化器會在不影響 SQL 執行結果的前提下,給你自動地優化。

創建索引

create index index_age_depid_name on emp(age,deptid,name);

# 查看emp的索引,檢測咱們創建索引是否成功
show index from emp
複製代碼

書寫SQL測試

EXPLAIN SELECT  * FROM emp WHERE emp.age=30;
複製代碼

image-20210529221955036

EXPLAIN SELECT  * FROM emp WHERE emp.age=30 and deptid=4;
複製代碼

image-20210529222442893

EXPLAIN SELECT  * FROM emp WHERE emp.age=30 and deptid=4 AND emp.name = 'abcd';
複製代碼

image-20210529222500414

5.二、最佳左前綴法則

5.2.一、索引失效狀況

explain select * from emp where  deptid = 4 and name = "xiaolin"
複製代碼

image-20210530143035021

    咱們能夠發現,這個時候索引失效了。

5.2.二、索引有效的狀況

explain select * from emp where  age = 18 and deptid = 4
複製代碼

image-20210530143311904

5.2.三、總結

    查詢字段與索引字段順序的不一樣會致使,索引沒法充分使用,甚至索引失效!使用複合索引,須要遵循最佳左前綴法則,即若是索引了多列,要遵照最左前綴法則。指的是查詢從索引的最左前列開始而且不跳過索引中的列。

    過濾條件要使用索引必須按照索引創建時的順序,依次知足,一旦跳過某個字段,索引後面的字段都沒法被使用。

5.三、不要在索引列上作任何計算

explain select * from emp where  age +1  = 18  and deptid = 4
複製代碼

image-20210530143910916

    咱們能夠發現,這個時候索引失效了,由於咱們在索引列——age上進行了+1的操做,咱們不能在索引列上作任何操做(計算、函數、(自動 or 手動)類型轉換),由於會致使索引失效而轉向全表掃描。

5.四、儘可能使用覆蓋索引

    咱們先看不適用覆蓋索引的狀況。

explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=5 AND emp.name = 'xiaolin';
複製代碼

image-20210530145053614

    再看使用到了覆蓋索引的狀況。

image-20210530145133951

    出現了一個Using Index,說明性能獲得了提高。查詢列和索引列一直,**不要寫 select ***

5.五、儘可能不要出現前綴模糊匹配

    在平常的使用過程當中,模糊匹配能夠說是使用不少的關鍵字了,在使用的過程當中,咱們須要避免使用前綴的模糊匹配,由於會形成索引失效。也就是說like進行以常量開頭,不要以%開頭

# 先給name字段加上一個索引
create index index_name on emp(name);

# 測試後綴模糊匹配
explain select * from emp where  name like  "a%";
複製代碼

image-20210530145922716

# 測試前綴模糊匹配
explain select * from emp where  name like  "%a";
複製代碼

image-20210530145954424

# 測試先後都模糊匹配
explain select * from emp where  name like  "%a";
複製代碼

image-20210530150158598

    咱們能夠發現,只要是出現了前綴的模糊匹配的時候,都會出現索引失效的問題。若是必定須要使用%開頭的模糊查詢,咱們可使用索引覆蓋來必定程度提升性能。

5.六、減小使用or

# 咱們使用or的時候也很容易形成索引失效的問題。
explain select * from emp where  age = 18  or deptid = 4
複製代碼

image-20210530150413315

    若是咱們在實際開發中,須要使用到or的話,咱們可使用 union all 或者 union 來替代。

# 使用union all替代
explain select * from emp where  age = 18  union all select * from emp where  deptid = 4;
複製代碼

image-20210530150635887

# 使用union替代
explain select * from emp where  age = 18  union select * from emp where  deptid = 4;
複製代碼

image-20210530150658153

5.八、儘可能不要使用顯示、隱式類型轉換

# 先試一下正常狀況
explain select * from emp where name="123";
複製代碼

image-20210530161451102

# 再試試索引失效狀況
explain select * from emp where name=123;
複製代碼

image-20210530161503769

索引失效的緣由是由於mysql底層會把int類型的123轉化爲varchar類型的123,索引失效。

5.七、總結

全職匹配我最愛,最左前綴要遵照。 帶頭大哥不能死,中間兄弟不能斷。 索引列上少計算,範圍以後全失效。 LIKE 百分寫最右,覆蓋索引不寫*。 不等空值還有 OR,索引影響要注意。 VAR 引號不可丟,SQL 優化有訣竅。

6、多表SQL優化

6.一、建表語句

create table teacher2
(
	tid int(4) primary key,
	cid int(4) not null
);

insert into teacher2 values(1,2);
insert into teacher2 values(2,1);
insert into teacher2 values(3,3);

create table course2
(
	cid int(4) ,
	cname varchar(20)
);

insert into course2 values(1,'java');
insert into course2 values(2,'python');
insert into course2 values(3,'kotlin');
commit;
複製代碼

6.二、left join

    當咱們進行連表查詢的時候,會想到一個問題,索引往哪張表加?

explain select *from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java';
複製代碼

image-20210530153441436

    通常狀況下,咱們把數據量小的表放在左邊,數據量大的表放在右邊,在進行連表查詢的時候,是左表驅動右表,也就是數據量小的表驅動數據量大的表,這是由於這條SQL查詢的底層,其實是兩個循環,一個外層循環,一個內層循環,在開發中,通常是將數據小的循環放外層,數據大的循環放內存。

    索引創建在常用的字段上,因此可得,若是是左外鏈接,索引創建在左表的字段,右外鏈接,索引創建在右表的字段。

    按照規則,咱們給teacher2這張表加上索引。

alter table teacher2 add index index_teacher2_cid(cid);
# 再次執行
explain select *from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java';
複製代碼

image-20210530153655103

    咱們能夠發現,有一張表已經用上了索引了。通常來講,where後面的字段也要加索引,因而咱們進一步優化。

# 給cname字段加上索引
alter table course2 add index index_course2_cname(cname);
# 再次執行
explain select *from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java';
複製代碼

image-20210530154000937

7、其餘的優化方法

7.一、exist

    exist語法是將主查詢的結果,放到子查詢的進行校驗(判斷子查詢是否有數據,若是有數據則校驗成功),若是符合校驗就保留數據。

select tname from teacher where exists (select * from teacher);
# 等價於
select * from teacher;
複製代碼
  1. 若是主查詢的數據集大,用in
  2. 若是子查詢的數據集很大,用exist

7.二、order by

7.2.一、MySQL的排序算法

    咱們通常使用order by的時候都會出現using filesort。using filesort有兩種算法:

  1. 雙路排序:MySQL4.1以前默認使用雙路排序,所謂的雙路就是掃描2次磁盤。第一次從磁盤中讀取排序字段,在buffer緩衝區對排序字段進行排序。第二次掃描其餘字段。這種兩次IO是很消耗性能的。
  2. 單路排序:MySQL4.1以後,爲了減小IO訪問次數,就改成了單路排序。他只讀取一次所有字段,在buffer中挑出排序字段進行排序。但
# 單位是字節,若是max_length_for_sort_data值過低(須要排序的總大小超過了max_length_for_sort_data定義的字節數),MySQL會自動從單路切換到雙路。
set max_length_for_sort_data = 2048;
複製代碼

7.2.1.一、雙路排序

    MySQL 4.1 以前是使用雙路排序,字面意思就是兩次掃描磁盤,最終獲得數據,第一次讀取行指針和 orderby 列,對他們進行排序,而後第二次掃描已經排序好的列表,按照列表中的值從新從列表中讀取對應的數據出。從磁盤取排序字段,在 buffer 進行排序,再從磁盤取其餘字段。

    簡單來講,取一批數據,要對磁盤進行了兩次掃描,衆所周知,I\O 是很耗時的,因此在 mysql4.1 以後,出現了第二種改進的算法,就是單路排序。

7.2.1.二、單路排序

    從磁盤讀取查詢須要的全部列,按照 order by 列在 buffer 對它們進行排序,而後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取數據。而且把隨機 IO 變成了順序 IO,可是它會使用更多的空間, 由於它把每一行都保存在內存中了。

7.2.1.三、單路排序存在的問題

    單路排序會有必定的隱患,他有可能不是一次IO,多是屢次IO。由於若是數據量太大的話會進行數據拆分,拆分紅屢次在buffer中進行排序,分片讀取,屢次讀取。咱們能夠經過sql語句來調大buffer的大小。

7.2.二、提升order by查詢的策略

7.2.2.一、增大 sort_butter_size 參數的設置

    無論用哪一種算法,提升這個參數都會提升效率,固然,要根據系統的能力去提升,由於這個參數是針對每一個進程的1M-8M 之間調整。

7.2.2.二、增大 max_length_for_sort_data 參數的設置

    mysql 使用單路排序的前提是排序的字段大小要小於max_length_for_sort_data。

    提升這個參數,會增長用改進算法的機率。可是若是設的過高,數據總容量超出 sort_buffer_size 的機率就增大,明顯症狀是高的磁盤 I/O 活動和低的處理器使用率。(1024-8192 之間調整)。

7.2.2.三、減小 select 後面的查詢的字段

    當 Query 的字段大小總和小於 max_length_for_sort_data 並且排序字段不是 TEXT|BLOB 類型時,會用改進後的算法——單路排序, 不然用老算法——多路排序。

    兩種算法的數據都有可能超出 sort_buffer 的容量,超出以後,會建立 tmp 文件進行合併排序,致使屢次 I/O,可是用單路排序算法的風險會更大一些,因此要提升 sort_buffer的大小。

    因此千萬不要使用select * ...;

7.2.2.四、使用覆蓋索引

    SQL 只須要經過索引就能夠返回查詢所須要的數據,而沒必要經過二級索引查到主鍵以後再去查詢數據。

7.2.2.五、保證排序的一致性

    咱們要保證所有的排序字段排序的一致性,要麼所有升序,要麼所有降序,不要出現某些部分升序,某些部分降序。

8、慢查詢日誌

8.一、慢查詢日誌

8.1.一、是什麼

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

    具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日誌中。long_query_time的默認值爲10,意思是運行10秒以上的語句。

    由他來查看哪些SQL超出了咱們的最大忍耐時間值,好比一條sql執行超過5秒鐘,咱們就算慢SQL,但願能收集超過5秒的sql,結合以前explain進行全面分析。

8.1.二、怎麼用

    默認狀況下,MySQL 數據庫沒有開啓慢查詢日誌,須要咱們手動來設置這個參數。

    固然,若是不是調優須要的話,通常不建議啓動該參數,由於開啓慢查詢日誌會或多或少帶來必定的性能影響。慢查詢日誌支持將日誌記錄寫入文件。通常在開發的時候打開,上線部署的時候關閉。

# 檢查是否開啓了慢查詢日誌,默認是off表示未開啓。
show variables like '%slow_query_log';
複製代碼

image-20210620160710116

# 臨時開啓,在內存中開啓,MySQL服務關閉時就關閉了
set global slow_query_log = 1;
複製代碼

image-20210620160829916

# 永久開啓,須要在MySQL的配置文件中進行編輯
# 進入MySQL的配置文件
vim /etc/my.cnf
複製代碼

在[mysqld]中添加兩行配置。

image-20210620161452157

# 開啓慢查詢日誌
slow_query_log=1
# 指定慢查詢日誌的存放路徑
slow_query_log_file=/var/lib/mysql/localhost-slow.log
複製代碼

image-20210620161607808

# 查詢慢查詢的閾值
show variables like '%long_query_time%';
複製代碼

image-20210620161904875

# 設置慢查詢閾值
# 臨時設置,設置完畢後,須要從新登錄後才生效
set global long_query_time = 5;
複製代碼

image-20210620162128482

# 永久開啓,須要在MySQL的配置文件中進行編輯,步驟和設置是否開啓慢查詢相同,只是寫的參數不一樣。
# 進入MySQL的配置文件
vim /etc/my.cnf
# 在[mysqld]下面追加
long_query_time=3
複製代碼
# 查詢超過慢查詢閾值的sql數量
# 睡眠4s,模擬一條超過了4s的SQL
select sleep(4);
show global status like '%slow_queries%';
複製代碼

image-20210620163625563

# 若是咱們想知道具體是哪條SQL的話,咱們須要去剛剛指定的慢查詢日誌文件中進行查詢
cat /var/lib/mysql/localhost-slow.log
複製代碼

image-20210620164508302

8.二、日誌分析工具 mysqldumpslow

    咱們能夠發現用原生的慢查詢日誌十分不友好,咱們能夠經過mysql自帶的日誌分析工具 mysqldumpslow來分析慢查詢。

# 在Linux中查看mysqldumpslow的幫助信息
mysqldumpslow --help
複製代碼
參數 描述
-s 是表示按照何種方式排序
c 訪問次數
l 鎖定時間
r 返回記錄
t 查詢時間
al 平均鎖定時間
ar 平均返回記錄數
at 平均查詢時間
-t 返回前面多少條的數據
-g 後邊搭配一個正則匹配模式,大小寫不敏感的
# 獲得返回記錄集最多的 10 個 SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
# 獲得訪問次數最多的 10 個 SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
# 獲得按照時間排序的前 10 條裏面含有左鏈接的查詢語句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log
# 另外建議在使用這些命令時結合 | 和 more 使用 ,不然有可能出現爆屏狀況
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
複製代碼

9、鎖

9.一、什麼是鎖機制

    在MySQL中有不少種類型的鎖,好比咱們最熟悉的行鎖,那這裏有個問題,爲何MySQL中會有行鎖呢?其實緣由就是:MySQL要保證數據的一致性。當數據 update 時首先要進行當前讀(讀取最新的數據)獲得數據,並且要保證查出來的數據到更改完成的這段時間內數據不會被其餘事務更改。這樣的話你的 update 語句執行獲得的結果和語義上是「一致的」。

9.二、鎖的分類

  • 根據操做類型分:
    1. 讀鎖(共享鎖):對同一個數據,多個讀操做能夠同時進行,互不干擾。
    2. 寫鎖(互斥鎖):若是當前寫操做沒有完畢,則沒法進行其餘的讀(至關於A在買衣服的時候把本來在前臺展現的衣服帶到了是試衣間裏面了,B連看都沒法看了)寫操做。
  • 操做範圍:
    1. 表鎖:一次性鎖一整張表,對一張表總體枷鎖,粒度粗。MyISAM存儲引擎使用的是表鎖,開銷小,加鎖快,無死鎖,可是鎖的範圍大,容易發生鎖衝突,併發度低。
    2. 行鎖:一次性對一條數據加鎖,粒度細不容易發生衝突,InnoDB存儲引擎使用的是行鎖,開銷大,加鎖慢,容易出現死鎖,鎖的範圍小,併發度高很小几率發生髒讀、幻讀、不可重複讀等高併發問題。
    3. 頁鎖

9.三、鎖的操做

/* MYSQL/SQLSERVER 支持自增,Oracle 須要藉助於序列來實現自增 */
create table tablelock
(
id int primary key auto_increment,
name varchar(20)
) engine myisam;

insert into tablelock(name) values('a1');
insert into tablelock(name) values('a2');
insert into tablelock(name) values('a3');
insert into tablelock(name) values('a4');
insert into tablelock(name) values('a5');
複製代碼

9.3.一、表鎖

9.3.1.一、加讀/寫鎖

# 給表加讀鎖或者寫鎖,能夠給多張表一塊兒加,語法格式爲
lock table1 read/writelock table2 read/write;
複製代碼

9.3.1.二、查看鎖

# 查看加鎖的表,1表明加了鎖
show open tables;
複製代碼

9.3.1.三、釋放鎖

# 釋放鎖
unlock tables;
複製代碼

9.3.1.四、分析表鎖定的嚴重程度

show status like 'table%';
複製代碼

    他的結果有兩行數據:

  • Table_locks_immedicate:表示能夠馬上獲取到的鎖數量

  • Tbale_locks_waited:表示須要等待的表鎖數,他的值越大說明鎖競爭越激烈

通常建議用Table_locks_immedicate/Tbale_locks_waited的值來衡量,若是大於5000,採用InnoDB引擎,不然使用MyISAM引擎。

9.3.1.五、總結

  • 若是某一個會話對A表加了讀鎖,則該會話能夠對A表進行讀操做,可是不能夠進行寫操做,該會話不能夠對除A表外的其餘表進行任何讀寫操做。

  • 簡單來講,給A表加了讀鎖,則當前會話只能對A表進行讀操做。

  • 其餘會話能夠對該表進行讀操做,也能夠進行寫操做,可是在進行寫操做的時候須要等帶加鎖的會話釋放鎖。

  • 對一個加寫鎖的會話,當前會話能夠對加了寫鎖的表進行任何增刪改查的操做,可是不能對其餘表進行增刪改查操做。其餘會話得等當前會話釋放了鎖以後才能夠進行增刪改查操做。

9.3.二、行鎖

    行鎖,一次鎖一行數據,所以 若是操做的是不一樣數據,則不干擾。

create table linelock
(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb;

insert into linelock(name) values('1');
insert into linelock(name) values('2');
insert into linelock(name) values('3');
insert into linelock(name) values('4');
insert into linelock(name) values('5');
複製代碼

    爲了研究行鎖,咱們須要暫時將自動提交關閉,方便咱們手動提交。

set autocommit = 0;
複製代碼

9.3.2.一、行鎖總結

  1. 表鎖是經過unlock tables來進行解鎖,也能夠經過事務解鎖 ;。而行鎖是經過事務(commit/rollback)解鎖。
  2. 若是會話x對某條數據a進行 DML操做(研究時:關閉了自動commit的狀況下),則其餘會話必須等待會話x結束事務(commit/rollback)後 才能對數據a進行操做。

9.3.2.二、行鎖的注意事項

若是沒有索引,則行鎖會轉爲表鎖。

show index from linelock ;
alter table linelock add index idx_linelock_name(name);
# 索引未失效
# 會話0進行寫操做
update linelock set name = 'ai' where name = '3' ;
# 會話1進行寫操做,不一樣的數據
update linelock set name = 'aiX' where name = '4' ;

# 索引失效(發生了索引類型轉換)
# 會話0進行寫操做
update linelock set name = 'ai' where name = 3 ;
# 會話1對不一樣的數據進行寫操做
update linelock set name = 'aiX' where name = 4 ;
複製代碼

    能夠發現,數據被阻塞了(加鎖),由於索引類型發生了類型轉換致使了索引失效,所以這次操做會從行鎖轉爲表鎖。

行鎖存在一種極爲特殊的狀況

值在範圍內,可是卻不存在,這種稱爲間隙鎖。好比咱們在linelock表中沒有id=7的數據,當咱們寫一條SQL的時候:update linelock set name ='x' where id >1 and id<9 ;,在中國where的範圍內,沒有id=7在這個範圍內,可是沒有id=7的數據,則id=7的數據成爲了間隙。MySQL會自動給間隙加鎖,名爲間隙鎖,同時他也是行鎖。即MySQL會自動給id=7的數據加間隙鎖(行鎖)。

9.3.2.三、關閉自動提交的四種方式

  1. set autocommit =0 ;
  2. start transaction ;
  3. begin ;
  4. 在sql後加for update。

    咱們也能夠在查詢的時候加行鎖,只需使用第四種方式。

# 經過for update對query語句進行加鎖。
select * from linelock where id =2 for update ;
複製代碼

9.3.2.四、行鎖分析

    咱們可使用SQL語句來分析行鎖。

show status like '%innodb_row_lock%' ;
複製代碼

    他有四個參數:

  1. Innodb_row_lock_current_waits :當前正在等待鎖的數量。
  2. Innodb_row_lock_time:等待總時長。從系統啓到如今 一共等待的時間。
  3. Innodb_row_lock_time_avg :平均等待時長。從系統啓到如今平均等待的時間。
  4. Innodb_row_lock_time_max :最大等待時長。從系統啓到如今最大一次等待的時間。
  5. Innodb_row_lock_waits :等待次數。從系統啓到如今一共等待的次數
相關文章
相關標籤/搜索