MySQL JOIN原理

来源:http://www.cnblogs.com/shengdimaya/archive/2017/07/05/7123069.html
-Advertisement-
Play Games

先看一下實驗的兩張表: 表comments,總行數28856 表comments_for,總行數57,comments_id是有索引的,ID列為主鍵。 以上兩張表是我們測試的基礎,然後看一下索引,comments_for這個表comments_id是有索引的,ID為主鍵。 最近被公司某一開發問道JO ...


先看一下實驗的兩張表:

表comments,總行數28856 表comments_for,總行數57,comments_id是有索引的,ID列為主鍵。 以上兩張表是我們測試的基礎,然後看一下索引,comments_for這個表comments_id是有索引的,ID為主鍵。 最近被公司某一開發問道JOIN了MySQL JOIN的問題,細數之下發下我對MySQL JOIN的理解並不是很深刻,所以也查看了很多文檔,最後在InsideMySQL公眾號看到了兩篇關於JOIN的分析,感覺寫的太好了,拿出來分享一下我對於JOIN的實際測試吧。下麵先介紹一下MySQL關於JOIN的演算法,總共分為三種(來源為InsideMySQL): MySQL是只支持一種JOIN演算法Nested-Loop Join(嵌套迴圈鏈接),不像其他商業資料庫可以支持哈希鏈接和合併連接,不過MySQL的Nested-Loop Join(嵌套迴圈鏈接)也是有很多變種,能夠幫助MySQL更高效的執行JOIN操作: (1)Simple Nested-Loop Join(圖片為InsideMySQL取來) 這個演算法相對來說就是很簡單了,從驅動表中取出R1匹配S表所有列,然後R2,R3,直到將R表中的所有數據匹配完,然後合併數據,可以看到這種演算法要對S表進行RN次訪問,雖然簡單,但是相對來說開銷還是太大了 (2)Index Nested-Loop Join,實現方式如下圖: 索引嵌套聯繫由於非驅動表上有索引,所以比較的時候不再需要一條條記錄進行比較,而可以通過索引來減少比較,從而加速查詢。這也就是平時我們在做關聯查詢的時候必須要求關聯欄位有索引的一個主要原因。 這種演算法在鏈接查詢的時候,驅動表會根據關聯欄位的索引進行查找,當在索引上找到了符合的值,再回表進行查詢,也就是只有當匹配到索引以後才會進行回表。至於驅動表的選擇,MySQL優化器一般情況下是會選擇記錄數少的作為驅動表,但是當SQL特別複雜的時候不排除會出現錯誤選擇。 在索引嵌套鏈接的方式下,如果非驅動表的關聯鍵是主鍵的話,這樣來說性能就會非常的高,如果不是主鍵的話,關聯起來如果返回的行數很多的話,效率就會特別的低,因為要多次的回表操作。先關聯索引,然後根據二級索引的主鍵ID進行回表的操作。這樣來說的話性能相對就會很差。 (3)Block Nested-Loop Join,實現如下: 在有索引的情況下,MySQL會嘗試去使用Index Nested-Loop Join演算法,在有些情況下,可能Join的列就是沒有索引,那麼這時MySQL的選擇絕對不會是最先介紹的Simple Nested-Loop Join演算法,而是會優先使用Block Nested-Loop Join的演算法。 Block Nested-Loop Join對比Simple Nested-Loop Join多了一個中間處理的過程,也就是join buffer,使用join buffer將驅動表的查詢JOIN相關列都給緩衝到了JOIN BUFFER當中,然後批量與非驅動表進行比較,這也來實現的話,可以將多次比較合併到一次,降低了非驅動表的訪問頻率。也就是只需要訪問一次S表。這樣來說的話,就不會出現多次訪問非驅動表的情況了,也只有這種情況下才會訪問join buffer。 在MySQL當中,我們可以通過參數join_buffer_size來設置join buffer的值,然後再進行操作。預設情況下join_buffer_size=256K,在查找的時候MySQL會將所有的需要的列緩存到join buffer當中,包括select的列,而不是僅僅只緩存關聯列。在一個有N個JOIN關聯的SQL當中會在執行時候分配N-1個join buffer。 上面介紹完了,下麵看一下具體的列子 (1)全表JOIN
EXPLAIN SELECT * FROM comments gc
JOIN comments_for gcf ON gc.comments_id=gcf.comments_id;

 

看一下輸出信息:   可以看到在全表掃描的時候comments_for 作為了驅動表,此事因為關聯欄位是有索引的,所以對索引idx_commentsid進行了一個全索引掃描去匹配非驅動表comments ,每次能夠匹配到一行。此時使用的就是Index Nested-Loop Join,通過索引進行了全表的匹配,我們可以看到因為comments_for 表的量級遠小於comments ,所以說MySQL優先選擇了小表comments_for 作為了驅動表。 (2)全表JOIN+篩選條件
SELECT * FROM comments gc
JOIN comments_for gcf ON gc.comments_id=gcf.comments_id
WHERE gc.comments_id =2056

 

此時使用的是Index Nested-Loop Join,先對驅動表comments 的主鍵進行篩選,符合一條,對非驅動表comments_for 的索引idx_commentsid進行seek匹配,最終匹配結果預計為影響一條,這樣就是僅僅對非驅動表的idx_commentsid索引進行了一次訪問操作,效率相對來說還是非常高的。 (3)看一下關聯欄位是沒有索引的情況:
EXPLAIN SELECT * FROM comments gc
JOIN comments_for gcf ON gc.order_id=gcf.product_id

 

我們看一下執行計劃: 從執行計劃我們就可以看出,這個表JOIN就是使用了Block Nested-Loop Join來進行表關聯,先把comments_for (只有57行)這個小表作為驅動表,然後將comments_for 的需要的數據緩存到JOIN buffer當中,批量對comments 表進行掃描,也就是只進行一次匹配,前提是join buffer足夠大能夠存下comments_for的緩存數據。 而且我們看到執行計劃當中已經很明確的提示:Using where; Using join buffer (Block Nested Loop) 一般情況出現這種情況就證明我們的SQL需要優化了。 要註意的是這種情況下,MySQL也會選擇Simple Nested-Loop Join這種暴力的方法,我還沒搞懂他這個優化器是怎麼選擇的,但是一般是使用Block Nested-Loop Join,因為CBO是基於開銷的,Block Nested-Loop Join的性能相對於Simple Nested-Loop Join是要好很多的。 (4)看一下left join
EXPLAIN SELECT * FROM comments gc
LEFT JOIN comments_for gcf ON gc.comments_id=gcf.comments_id

 

看一下執行計劃: 這種情況,由於我們的關聯欄位是有索引的,所以說Index Nested-Loop Join,只不過當沒有篩選條件的時候會選擇第一張表作為驅動表去進行JOIN,去關聯非驅動表的索引進行Index Nested-Loop Join。 如果加上篩選條件gc.comments_id =2056的話,這樣就會篩選出一條對非驅動表進行Index Nested-Loop Join,這樣效率是很高的。 如果是下麵這種:
EXPLAIN SELECT * FROM comments_for gcf
LEFT JOIN comments gc ON gc.comments_id=gcf.comments_id
WHERE gcf.comments_id =2056

 

通過gcf表進行篩選的話,就會預設選擇gcf表作為驅動表,因為很明顯他進行過了篩選,匹配的條件會很少,具體可以看下執行計劃: 此,join基本上已經很明瞭了,未完待續中,歡迎大家指出錯誤,我會認真改正。。。。  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 右擊還存在的訂閱,生成腳本,有個過程sp_addpullsubscription_agent 執行,發現報錯說distribution agent 已經存在 執行: UPDATE dbo.MSreplication_subscriptions SET distribution_agent =NULL ...
  • GETDATE,datepart,DATEPART,DATEADD,DATEDIFF,DATENAME,YEAR,MONTH,DAY,CONVERT ...
  • 通過存儲過程獲取當前日期(或指定日期)所在的日期區間,包含年、月、周、日。 sql CREATE PROCEDURE [dbo].[GetDataPeriod] @defaultDate DATE=NULL, @period VARCHAR(1)=NULL, @startDate DATE OUTP ...
  • 一、確認防火牆沒有阻止3306埠(一般伺服器預設會屏蔽掉) windows防火牆例外設置方法 控制面板(右上角選擇查看方式為大圖標) 防火牆 高級設置 高級設置 出站規則 最右邊 新建規則“新建出站規則嚮導”,在嚮導的第一步,選擇要創建的防火牆規則類型,選擇第二個“埠”。 windows防火牆添 ...
  • jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewr ...
  • 在傳遞數據時,XML和JSON是最常用的數據格式,SQL Server從很早的版本就開始支持XML格式,而對於JSON格式,SQL Server從2016版本開始支持。大多數資料庫系統並沒有升級到SQL Server 2016版本,因此在傳遞格式化的數據時,通常還是使用XML格式。對我而言,查詢和解 ...
  • 1、確認mysql是否已安裝,有下麵的代碼可知 2、卸載已安裝的mysql,建議使用yum命令,因為yum命令可以自動刪除與mysql相關的依賴;如果使用rpm命令,則還需要手動去刪除和mysql相關的文件,比較麻煩 3、用whereis命令查找mysql相關的文件,因為步驟2用yum來卸載,所以m ...
  • 1. SVM基本知識 SVM(Support Vector Machine)是一個類分類器,能夠將不同類的樣本在樣本空間中進行分隔,分隔使用的面叫做分隔超平面。 比如對於二維樣本,分佈在二維平面上,此時超平面實際上是一條直線,直線上面是一類,下麵是另一類。定義超平面為: f(x)=w0+wTx 可以 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...