欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL之join查詢優(yōu)化方式

 更新時間:2023年03月12日 15:49:49   作者:布道  
這篇文章主要介紹了MySQL之join查詢優(yōu)化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

MySQL join查詢優(yōu)化

在日常的開發(fā)中,我們經(jīng)常遇到這樣情況:select * from TableA  inner join TableB...它響應速度一直很快的,隨著數(shù)據(jù)的增長,突然有一天開始很慢了。那該怎么破?

對,驅(qū)動表是突破口,

1. 那什么是驅(qū)動表呢?

  • 指定了聯(lián)接條件時,滿足查詢條件的記錄行數(shù)少的表為驅(qū)動表
  • 未指定聯(lián)接條件時,行數(shù)少的表為驅(qū)動表(Important!)

如果你搞不清楚該讓誰做驅(qū)動表、誰 join 誰,就別指定誰 left/right join 誰了,請交給 MySQL優(yōu)化器 運行時決定吧。

2. 復雜的sql怎么識別驅(qū)動表呢?

按經(jīng)驗談,使用EXPLAIN, 第一行出現(xiàn)的表就是驅(qū)動表。

3. 關(guān)聯(lián)查詢原理是怎樣的?

MySQL 表關(guān)聯(lián)的算法是 Nest Loop Join,是通過驅(qū)動表的結(jié)果集作為循環(huán)基礎(chǔ)數(shù)據(jù),然后一條一條地通過該結(jié)果集中的數(shù)據(jù)作為過濾條件到下一個表中查詢數(shù)據(jù),然后合并結(jié)果。

//例: user表10000條數(shù)據(jù),class表20條數(shù)據(jù)
select * from user u left join class c u.userid=c.userid

上面sql的后果就是需要用user表循環(huán)10000次才能查詢出來,而如果用class表驅(qū)動user表則只需要循環(huán)20次就能查詢出來。

4. 該如如何優(yōu)化?

優(yōu)化的目標是盡可能減少JOIN中Nested Loop的循環(huán)次數(shù),以此保證:永遠用小結(jié)果集驅(qū)動大結(jié)果集。

排序的字段也有影響,有條原則:對驅(qū)動表可以直接排序,對非驅(qū)動表(的字段排序)需要對循環(huán)查詢的合并結(jié)果(臨時表)進行排序!

5. 實例

explain select * from user u left join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id?
?WHERE 1=1 ORDER BY u.create_time DESC limit 0,10

夠復雜吧。假如,user表有千萬級記錄,class表要少得多,從執(zhí)行計劃的得知驅(qū)動表(數(shù)據(jù)到千萬級)。由于動用了“LEFT JOIN”,所以相當于已經(jīng)指定了驅(qū)動表。

如何優(yōu)化?

//優(yōu)化第一步:LEFT JOIN改為JOIN,對,直接 join!
explain select * from user u join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id?
?WHERE 1=1 ORDER BY u.create_time DESC limit 0,10
//優(yōu)化第二步:從上面執(zhí)行計劃得知, 有Using temporary(臨時表);Using filesort,解決方法是調(diào)整排序字段(借助前面講過排序的原則)
explain select * from user u join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id?
?WHERE 1=1 ORDER BY c.id DESC limit 0,10

總之,sql優(yōu)化中explain工具是非常重要的武器。

MySQL優(yōu)化(關(guān)聯(lián)查詢優(yōu)化)

準備數(shù)據(jù)

#分類
CREATE TABLE IF NOT EXISTS `class` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
);
#圖書
CREATE TABLE IF NOT EXISTS `book` (
`bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`bookid`)
);
 
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
 
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));

left join左外連接

看這個分析結(jié)果發(fā)現(xiàn):在 class 表上添加的索引起的作用不大。

結(jié)論: 

- **小表驅(qū)動大表**

  • - 小表:相對來說記錄較少的表
  • - 大表:相對來說記錄較多的表

- 驅(qū)動方式識別

  • left join:左邊驅(qū)動右邊(此時把小表放在左邊)
  • right join:右邊驅(qū)動左邊(此時把小表放在右邊)

- 加索引的方式:通常建議在大表(被驅(qū)動)的表加索引,效率提升更明顯。

- 原因:

  • 原因1:被驅(qū)動表加了索引之后,收益更大。從 ALL -> ref
  • 原因2:外連接首先讀取驅(qū)動表的全部數(shù)據(jù),被驅(qū)動只讀取滿足連接條件的數(shù)據(jù)。

inner join:MySQL會自動根據(jù)表中的數(shù)據(jù)選擇驅(qū)動表

小結(jié):

- 保證被驅(qū)動表的 join 字段被索引。join 字段就是作為連接條件的字段。

- left join 時,選擇小表作為驅(qū)動表(放左邊),大表作為被驅(qū)動表(放右邊)

- inner join 時,mysql 會自動將小結(jié)果集的表選為驅(qū)動表。

- 子查詢盡量不要放在被驅(qū)動表,衍生表建不了索引

- 能夠直接多表關(guān)聯(lián)的盡量直接關(guān)聯(lián),不用子查詢

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論