搜尋
首頁資料庫mysql教程深入了解MySQL中的join語句演算法(最佳化方法介紹)

這篇文章帶大家深入了解MySQL中的join語句演算法,聊聊join語句的最佳化方法,希望對大家有幫助!

深入了解MySQL中的join語句演算法(最佳化方法介紹)

一、join語句演算法

建立兩個表t1和t2

CREATE TABLE `t2` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`)
) ENGINE=InnoDB;

CREATE DEFINER=`root`@`%` PROCEDURE `idata`()
BEGIN
	declare i int;
  set i=1;
  while(i<p>這兩個表都有一個主鍵索引id和一個索引a,字段b上無索引。預存程序idata()往表t2插入了1000行數據,在表t1裡插入的是100行數據</p><h3 id="strong-Index-Nested-Loop-Join-strong"><strong>1、Index Nested-Loop Join</strong></h3>#<pre class="brush:php;toolbar:false">select * from t1 straight_join t2 on (t1.a=t2.a);

如果直接使用join語句,MySQL優化器可能會選擇表t1或t2作為驅動表,透過straight_join讓MySQL使用固定的連接方式執行查詢,在這個語句裡,t1是驅動表,t2是被驅動表
深入了解MySQL中的join語句演算法(最佳化方法介紹)
被驅動表t2的欄位a上有索引,join程序用上了這個索引,因此這個語句的執行流程是這樣的:

1.從表t1讀入一行資料R

2.從資料行R中,取出a字段到表t2裡去查找

3.取出表t2中滿足條件的行,跟R組成一行,作為結果集的一部分

4.重複執行步驟1到3,直到表t1的末尾循環結束

這個程序可以用上被驅動表的索引,稱為Index Nested-Loop Join ,簡稱NLJ

深入了解MySQL中的join語句演算法(最佳化方法介紹)

在這個流程裡:

#1.對驅動程式表t1做了全表掃描,這個過程需要掃描100行

2.而對於每一行R,根據a字段去表t2查找,走的是樹搜尋過程。由於我們建構的資料都是一一對應的,因此每次的搜尋過程都只掃描一行,也是總共掃描100行

3.所以,整個執行流程,總掃描行數是200

假設不使用join,只能用單表查詢:

1.執行select * from t1,查出表t1的所有數據,這裡有100行

2.循環遍歷這100行資料:

  • 從每一行R取出欄位a的值$R.a
  • 執行select * from t2 where a= $R.a
  • 把傳回的結果和R構成結果集的一行

這個查詢過程,也是掃描了200行,但是總共執行了101個語句,比直接join多了100次互動。客戶端還要自己拼接SQL語句和結果。這麼做還不如直接join好

深入了解MySQL中的join語句演算法(最佳化方法介紹)

在可以使用被驅動表的索引的情況下:

  • 使用join語句,效能比強行拆成多個單表執行SQL語句的效能要好
  • 如果使用join語句的話,需要讓小表做驅動表

2、Simple Nested-Loop Join

select * from t1 straight_join t2 on (t1.a=t2.b);

由於表t2的欄位b上沒有索引,因此每次到t2去匹配的時候,就要做一次全表掃描。這個演算法叫做Simple Nested-Loop Join

這樣算來,這個SQL請求就要掃描表t2多達100次,總共掃描100*100=10萬行

MySQL沒有使用這個Simple Nested-Loop Join演算法,而是使用了另一個叫作Block Nested-Loop Join的演算法,簡稱BNL

3、Block Nested-Loop Join

#被驅動表上沒有可用的索引,演算法的流程如下:

1.把表t1的資料讀入線程記憶體join_buffer中,由於這個語句中寫的是select *,因此是把整個表t1放入了記憶體

2.掃描表t2,把表t2中的每一行取出來,跟join_buffer中的資料作比對,滿足join條件的,作為結果集的一部分回傳

深入了解MySQL中的join語句演算法(最佳化方法介紹)

深入了解MySQL中的join語句演算法(最佳化方法介紹)

在這個過程中,對錶t1和表t2都做了一次全表掃描,因此總的掃描行數是1100。由於join_buffer是以無序數組的方式組織的,因此對錶t2中的每一行,都要做​​100次判斷,總共需要在內存中做的判斷次數是100*1000=10萬次

使用Simple Nested-Loop Join演算法進行查詢,掃描行數也是10萬行。因此,從時間複雜度上來說,這兩個演算法是一樣的。但是,Block Nested-Loop Join演算法的這10萬次判斷是記憶體操作,速度會快很多,效能也更好

假設小表的行數是N,大表的行數是M ,那麼在這個演算法裡:

1)兩個表格都做一次全表掃描,所以總的掃描行數是M N 

2)記憶體中的判斷次數是M ∗ N

這時候選擇大表還是小表做驅動表,執行耗時是一樣的

join_buffer的大小是由參數join_buffer_size設定的,預設值是256k。如果放不下表t1的所有資料話,策略很簡單,就是分段放

1)扫描表t1,顺序读取数据行放入join_buffer中,假设放到第88行join_buffer满了

2)扫描表t2,把t2中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回

3)清空join_buffer

4)继续扫描表t1,顺序读取最后的12行放入join_buffer中,继续执行第2步

深入了解MySQL中的join語句演算法(最佳化方法介紹)
由于表t1被分成了两次放入join_buffer中,导致表t2会被扫描两次。虽然分成两次放入join_buffer,但是判断等值条件的此时还是不变的

假设,驱动表的数据行数是N,需要分成K段才能完成算法流程,被驱动表的数据行数是M 。这里的K不是常数,N越大K 就会越大,因此把K表示为λ ∗ N ,λ的取值范围是(0,1)。所以,在这个算法的执行过程中:

1.扫描行数是N + λ ∗ N ∗ M

2.内存判断N ∗ M

考虑到扫描行数,N 小一些,整个算式的结果会更小。所以应该让小表当驱动表

4、能不能使用join语句?

1.如果可以使用Index Nested-Loop Join算法,也就是说可以用上被驱动表上的索引,其实是没问题的

2.如果使用Block Nested-Loop Join算法,扫描行数就会过多。尤其是在大表上的join操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种join尽量不要用

5、如果使用join,应该选择大表做驱动表还是选择小表做驱动表

1.如果是Index Nested-Loop Join算法,应该选择小表做驱动表

2.如果是Block Nested-Loop Join算法:

  • 在join_buffer_size足够大的时候,是一样的
  • 在join_buffer_size不够大的时候,应该选择小表做驱动表

在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成以后,计算参数join的各个字段的总数据量,数据量小的那个表,就是小表,应该作为驱动表

二、join语句优化

创建两个表t1、t2

create table t1(id int primary key, a int, b int, index(a));
create table t2 like t1;

CREATE DEFINER = CURRENT_USER PROCEDURE `idata`()
BEGIN
	declare i int;
  set i=1;
  while(i<=1000)do
    insert into t1 values(i, 1001-i, i);
    set i=i+1;
  end while;
  
  set i=1;
  while(i<=1000000)do
    insert into t2 values(i, i, i);
    set i=i+1;
  end while;

END;

在表t1中,插入了1000行数据,每一行的a=1001-id的值。也就是说,表t1中字段a是逆序的。同时,在表t2中插入了100万行数据

1、Multi-Range Read优化

Multi-Range Read(MRR)优化主要的目的是尽量使用顺序读盘

select * from t1 where a>=1 and a<p>主键索引是一棵B+树,在这棵树上,每次只能根据一个主键id查到一行数据。因此,回表是一行行搜索主键索引的</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/5c16d719738fb23caf97d38d00c1644b-5.png?x-oss-process=image/resize,p_40" class="lazy" alt="深入了解MySQL中的join語句演算法(最佳化方法介紹)"></p><p>如果随着a的值递增顺序查找的话,id的值就变成随机的,那么就会出现随机访问,性能相对较差</p><p><strong>因为大多数的数据都是按照主键递增顺序插入得到的,所以如果按照主键的递增顺序查询,对磁盘的读比较接近顺序读,能够提升读性能</strong></p><p>这就是MRR优化的设计思路,语句的执行流程如下:</p><p>1.根据索引a,定位到满足条件的记录,将id值放入read_rnd_buffer中</p><p>2.将read_rnd_buffer中的id进行递增排序</p><p>3.排序后的id数组,依次到主键id索引中查记录,并作为结果返回</p><p>read_rnd_buffer的大小是由read_rnd_buffer_size参数控制的。如果步骤1中,read_rnd_buffer放满了,就会先执行完步骤2和3,然后清空read_rnd_buffer。之后继续找索引a的下个记录,并继续循环</p><p>如果想要稳定地使用MRR优化的话,需要设置<code>set optimizer_switch="mrr_cost_based=off"</code></p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/5c16d719738fb23caf97d38d00c1644b-6.png?x-oss-process=image/resize,p_40" class="lazy" alt="深入了解MySQL中的join語句演算法(最佳化方法介紹)"></p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/3833cfce76c7a34447aff9e3973bd58a-7.png?x-oss-process=image/resize,p_40" class="lazy" alt="深入了解MySQL中的join語句演算法(最佳化方法介紹)"><br> explain结果中,Extra字段多了Using MRR,表示的是用上了MRR优化。由于在read_rnd_buffer中按照id做了排序,所以最后得到的结果也是按照主键id递增顺序的</p><p><strong>MRR能够提升性能的核心在于,这条查询语句在索引a上做的是一个范围查询,可以得到足够多的主键id。这样通过排序以后,再去主键索引查数据,才能体现出顺序性的优势</strong></p><h3 id="strong-Batched-Key-Access-strong"><strong>2、Batched Key Access</strong></h3><p>MySQL5.6引入了Batched Key Access(BKA)算法。这个BKA算法是对NLJ算法的优化</p><p>NLJ算法流程图:</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/3833cfce76c7a34447aff9e3973bd58a-8.png?x-oss-process=image/resize,p_40" class="lazy" alt="深入了解MySQL中的join語句演算法(最佳化方法介紹)"></p><p>NLJ算法执行的逻辑是从驱动表t1,一行行地取出a的值,再到被驱动表t2去做join</p><p>BKA算法流程图:</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/3833cfce76c7a34447aff9e3973bd58a-9.png?x-oss-process=image/resize,p_40" class="lazy" alt="深入了解MySQL中的join語句演算法(最佳化方法介紹)"></p><p>BKA算法执行的逻辑是把表t1的数据取出来一部分,先放到一个join_buffer,一起传给表t2。在join_buffer中只会放入查询需要的字段,如果join_buffer放不下所有数据,就会将数据分成多段执行上图的流程</p><p>如果想要使用BKA优化算法的话,执行SQL语句之前,先设置</p><pre class="brush:php;toolbar:false">set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

其中前两个参数的作用是启用MRR,原因是BKA算法的优化要依赖与MRR

3、BNL算法的性能问题

InnoDB对Buffer Pool的LRU算法做了优化,即:第一次从磁盘读入内存的数据页,会先放在old区域。如果1秒之后这个数据页不再被访问了,就不会被移动到LRU链表头部,这样对Buffer Pool的命中率影响就不大

如果一个使用BNL算法的join语句,多次扫描一个冷表,而且这个语句执行时间超过1秒,就会在再次扫描冷表的时候,把冷表的数据页移到LRU链表头部。这种情况对应的,是冷表的数据量小于整个Buffer Pool的3/8,能够完全放入old区域的情况

如果这个冷表很大,就会出现另外一种情况:业务正常访问的数据页,没有机会进入young区域。

由于优化机制的存在,一个正常访问的数据页,要进入young区域,需要隔1秒后再次被访问到。但是,由于join语句在循环读磁盘和淘汰内存页,进入old区域的数据页,很可能在1秒之内就被淘汰了。这样就会导致MySQL实例的Buffer Pool在这段时间内,young区域的数据页没有被合理地淘汰

BNL算法对系统的影响主要包括三个方面:

1.可能会多次扫描被驱动表,占用磁盘IO资源

2.判断join条件需要执行MN次对比,如果是大表就会占用非常多的CPU资源

3.可能会导致Buffer Pool的热数据被淘汰,影响内存命中率

4、BNL转BKA

一些情况下,我们可以直接在被驱动表上建索引,这时就可以直接转成BKA算法了

如果碰到一些不适合在被驱动表上建索引的情况,可以考虑使用临时表。大致思路如下:

select * from t1 join t2 on (t1.b=t2.b) where t2.b>=1 and t2.b<p>1)把表t2中满足条件的数据放在临时表tmp_t中</p><p>2)为了让join使用BKA算法,给临时表tmp_t的字段b加上索引</p><p>3)让表t1和tmp_t做join操作</p><p>SQL语句写法如下:</p><pre class="brush:php;toolbar:false">create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;insert into temp_t select * from t2 where b>=1 and b<h3 id="strong-扩展hash-join-strong"><strong>5、扩展hash join</strong></h3><p>MySQL的优化器和执行器不支持哈希join,可以自己实现在业务端,实现流程大致如下:</p><p>1.<code>select * from t1;</code>取得表t1的全部1000行数据,在业务端存入一个hash结构</p><p>2.<code>select * from t2 where b>=1 and b获取表t2中满足条件的2000行数据</code></p><p>3.把这2000行数据,一行一行地取到业务端,到hash结构的数据表中寻找匹配的数据。满足匹配的条件的这行数据,就作为结果集的一行</p><p>【相关推荐:<a href="https://www.php.cn/course/list/51.html" target="_blank" textvalue="mysql视频教程">mysql视频教程</a>】</p>

以上是深入了解MySQL中的join語句演算法(最佳化方法介紹)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:csdn。如有侵權,請聯絡admin@php.cn刪除
解釋InnoDB緩衝池及其對性能的重要性。解釋InnoDB緩衝池及其對性能的重要性。Apr 19, 2025 am 12:24 AM

InnoDBBufferPool通過緩存數據和索引頁來減少磁盤I/O,提升數據庫性能。其工作原理包括:1.數據讀取:從BufferPool中讀取數據;2.數據寫入:修改數據後寫入BufferPool並定期刷新到磁盤;3.緩存管理:使用LRU算法管理緩存頁;4.預讀機制:提前加載相鄰數據頁。通過調整BufferPool大小和使用多個實例,可以優化數據庫性能。

MySQL與其他編程語言:一種比較MySQL與其他編程語言:一種比較Apr 19, 2025 am 12:22 AM

MySQL与其他编程语言相比,主要用于存储和管理数据,而其他语言如Python、Java、C 则用于逻辑处理和应用开发。MySQL以其高性能、可扩展性和跨平台支持著称,适合数据管理需求,而其他语言在各自领域如数据分析、企业应用和系统编程中各有优势。

學習MySQL:新用戶的分步指南學習MySQL:新用戶的分步指南Apr 19, 2025 am 12:19 AM

MySQL值得學習,因為它是強大的開源數據庫管理系統,適用於數據存儲、管理和分析。 1)MySQL是關係型數據庫,使用SQL操作數據,適合結構化數據管理。 2)SQL語言是與MySQL交互的關鍵,支持CRUD操作。 3)MySQL的工作原理包括客戶端/服務器架構、存儲引擎和查詢優化器。 4)基本用法包括創建數據庫和表,高級用法涉及使用JOIN連接表。 5)常見錯誤包括語法錯誤和權限問題,調試技巧包括檢查語法和使用EXPLAIN命令。 6)性能優化涉及使用索引、優化SQL語句和定期維護數據庫。

MySQL:初學者的基本技能MySQL:初學者的基本技能Apr 18, 2025 am 12:24 AM

MySQL適合初學者學習數據庫技能。 1.安裝MySQL服務器和客戶端工具。 2.理解基本SQL查詢,如SELECT。 3.掌握數據操作:創建表、插入、更新、刪除數據。 4.學習高級技巧:子查詢和窗口函數。 5.調試和優化:檢查語法、使用索引、避免SELECT*,並使用LIMIT。

MySQL:結構化數據和關係數據庫MySQL:結構化數據和關係數據庫Apr 18, 2025 am 12:22 AM

MySQL通過表結構和SQL查詢高效管理結構化數據,並通過外鍵實現表間關係。 1.創建表時定義數據格式和類型。 2.使用外鍵建立表間關係。 3.通過索引和查詢優化提高性能。 4.定期備份和監控數據庫確保數據安全和性能優化。

MySQL:解釋的關鍵功能和功能MySQL:解釋的關鍵功能和功能Apr 18, 2025 am 12:17 AM

MySQL是一個開源的關係型數據庫管理系統,廣泛應用於Web開發。它的關鍵特性包括:1.支持多種存儲引擎,如InnoDB和MyISAM,適用於不同場景;2.提供主從復制功能,利於負載均衡和數據備份;3.通過查詢優化和索引使用提高查詢效率。

SQL的目的:與MySQL數據庫進行交互SQL的目的:與MySQL數據庫進行交互Apr 18, 2025 am 12:12 AM

SQL用於與MySQL數據庫交互,實現數據的增、刪、改、查及數據庫設計。 1)SQL通過SELECT、INSERT、UPDATE、DELETE語句進行數據操作;2)使用CREATE、ALTER、DROP語句進行數據庫設計和管理;3)複雜查詢和數據分析通過SQL實現,提升業務決策效率。

初學者的MySQL:開始數據庫管理初學者的MySQL:開始數據庫管理Apr 18, 2025 am 12:10 AM

MySQL的基本操作包括創建數據庫、表格,及使用SQL進行數據的CRUD操作。 1.創建數據庫:CREATEDATABASEmy_first_db;2.創建表格:CREATETABLEbooks(idINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(100)NOTNULL,authorVARCHAR(100)NOTNULL,published_yearINT);3.插入數據:INSERTINTObooks(title,author,published_year)VA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具