【慢SQL性能優化】 一條SQL的生命周期

来源:https://www.cnblogs.com/Jcloud/archive/2023/11/08/17816654.html
-Advertisement-
Play Games

本文采用一張簡單的架構圖說明瞭MySQL查詢中使用的組件和組件間關係。解析了一條sql語句從客戶端請求mysql伺服器到返回給客戶端的整個生命周期流程。 ...


一、 一條簡單SQL在MySQL執行過程

一張簡單的圖說明下,MySQL架構有哪些組件和組建間關係,接下來給大家用SQL語句分析

例如如下SQL語句

SELECT department_id FROM employee WHERE name = 'Lucy' AND age > 18 
GROUP BY department_id


其中name為索引,我們按照時間順序來分析一下

  1. 客戶端:如MySQL命令行工具、Navicat、DBeaver或其他應用程式發送SQL查詢到MySQL伺服器。

  2. 連接器:負責與客戶端建立連接、管理連接和維護連接。當客戶端連接到MySQL伺服器時,連接器驗證客戶端的用戶名和密碼,然後分配一個線程來處理客戶端的請求。

  3. 查詢緩存:查詢緩存用於緩存先前執行過的查詢及其結果。當收到新的查詢請求時,MySQL首先檢查查詢緩存中是否已有相同的查詢及其結果。如果查詢緩存中有匹配的查詢結果,MySQL將直接返回緩存的結果,而無需再次執行查詢。但是,如果查詢緩存中沒有匹配的查詢結果,MySQL將繼續執行查詢。

  4. 分析器:

◦ 解析查詢語句,檢查語法。

◦ 驗證表名和列名的正確性。

◦ 生成查詢樹。

  1. 優化器:分析查詢樹,考慮各種執行計劃,估算不同執行計劃的成本,選擇最佳的執行計劃。在這個例子中,優化器可能會選擇使用name索引進行查詢,因為name是索引列。

  2. 執行器:根據優化器選擇的執行計劃,向存儲引擎發送請求,獲取滿足條件的數據行。

  3. 存儲引擎(如InnoDB):

◦ 負責實際執行索引掃描,如在employee表的name索引上進行等值查詢,因查詢全部列,涉及到回表訪問磁碟。

◦ 在訪問磁碟之前,先檢查InnoDB的緩衝池(Buffer Pool)中是否已有所需的數據頁。如果緩衝池中有符合條件的數據頁,直接使用緩存的數據。如果緩衝池中沒有所需的數據頁,從磁碟載入數據頁到緩衝池中。

  1. 執行器:

◦ 對於每個找到的記錄,再次判斷記錄是否滿足索引條件name。這是因為基於索引條件載入到記憶體中是數據頁,數據頁中也有可能包含不滿足索引條件的記錄,所以還要再判斷一次name條件,滿足name條件則繼續判斷age > 18過濾條件。

◦ 根據department_id對滿足條件的記錄進行分組。

◦ 執行器將處理後的結果集返回給客戶端。

在整個查詢執行過程中,這些組件共同協作以高效地執行查詢。客戶端負責發送查詢,連接器管理客戶端連接,查詢緩存嘗試重用先前查詢結果,解析器負責解析查詢,優化器選擇最佳執行計劃,執行器執行優化器選擇的計劃,存儲引擎(如InnoDB)負責管理數據存儲和訪問。這些組件的協同作用使得MySQL能夠高效地執行查詢並返回結果集。

根據索引列過濾條件載入索引的數據頁到記憶體這個操作是存儲引擎做的。載入到記憶體中之後,執行器會進行索引列和非索引列的過濾條件判斷。

二、 查詢SQL關鍵字執行順序

執行順序,如下:

1、對存儲引擎的操作

(1)FROM:用於查詢SQL的數據表。執行器會根據優化器選擇的執行計劃從存儲引擎中獲取相關表的數據。

(2)ON: 與JOIN一起使用,用於指定連接條件。執行器會根據ON給定的條件條件從存儲引擎獲取匹配條件的記錄。如果連接條件涉及到索引列,存儲引擎會使用索引進行優化。

(3)JOIN:指定表之間連接方式(如INNER JOIN,LEFT JOIN等)。執行器會根據優化器選擇的執行計劃,從存儲引擎中獲取連接表數據。然後執行器根據JOIN連接類型和ON連接條件,對數據連接處理。

(4)WHERE:執行器對從存儲引擎返回的數據進行過濾,只保留滿足WHERE子句條件的記錄。過濾條件如有索引,存儲引擎層會通過索引過濾後返回。

2、對返回結果集的操作

(5)GROUP BY:執行器對滿足WHERE條件的記錄按照GROUP BY指定的列分組。

(6)HAVING:執行器在執行分組後,根據HAVING條件對分組後的記錄再次過濾。

(7)SELECT:執行器根據優化器選擇的執行計劃和指定列獲取查詢結果。

(8)DISTINCT:執行器對查詢結果進行去重,只返回不重覆的記錄。

(9)ORDER BY:執行器對查詢結果按照ORDER BY子句中指定的列進行排序。

(10)LIMIT:執行器根據LIMIT子句中指定的限制條件對查詢結果進行截斷,只返回部分記錄

三、表關聯查詢SQL在MySQL中的執行過程

SELECT s.id, s.name, s.age, es.subject, es.score 
FROM employee s JOIN employee_score es ON s.id = es.employee_id 
WHERE s.age >18 AND es.subject_id =3 AND es.score >80;


這個例子中,subject_idscore是聯合索引,age是索引。 我們按照時間順序來分析一下

  1. 連接器:當客戶端連接到MySQL伺服器時,連接器負責建立和管理連接。它驗證客戶端提供的用戶名和密碼,確定客戶端具有相應的許可權,然後建立連接。

  2. 查詢緩存:MySQL伺服器在處理查詢之前,會先檢查查詢緩存。如果查詢緩存中已經存在該結果集,伺服器將直接返回緩存中的結果。

  3. 解析器:解析並檢查SQL語法正確性。解析器會將查詢語句分解成多個組成部分,例如表、列、條件等。在這個示例中,解析器會識別出涉及的表(employeeemployee_score)以及需要的列(id、name、age、subject、score)。

  4. 優化器:根據解析器提供的信息生成執行計劃。優化器會分析多種可能的執行策略,並選擇成本最低的策略。在這個示例中,優化器會選擇age索引和subject_idscore的聯合索引。對於連接操作,優化器還要決定連接策略,例如是否使用Nested-Loop JoinHash Join等一些連接策略。優化器還會根據表的大小、索引、查詢條件和統計信息來決定哪張表作為驅動表,以及選擇最佳的連接策略。例如,如果兩個表的大小差異很大,**Nested-Loop Join**可能是一個好的選擇,而對於大小相似的兩個表,**Hash Join****Sort-Merge Join**可能更加高效。

  5. 執行器:根據優化器生成的執行計劃執行查詢,向存儲引擎發送請求,獲取滿足條件的數據行。

  6. 存儲引擎(如InnoDB):管理數據存儲和檢索。存儲引擎首先接收來自執行器的請求,該請求可能是基於優化器的執行計劃。

◦ 存儲引擎首先接收來自執行器的請求。請求可能包括獲取滿足查詢條件的數據行,以及使用哪種掃描方法(如全表掃描或索引掃描)。

◦ 假設執行器已經決定使用索引掃描。在這個示例中,存儲引擎可能會先對employee表進行索引掃描(使用age索引),然後對employee_score表進行索引掃描(使用subject_idscore的聯合索引)。

◦ 存儲引擎會根據請求查詢相應的索引。在employee索引中會找到滿足age > 18條件的記錄。在employee_score索引中找到滿足subject_id = 3 AND score > 80條件的記錄。

◦ 一旦找到了滿足條件的記錄,存儲引擎需要將這些記錄所在的數據頁從磁碟載入到記憶體中。存儲引擎首先檢查緩衝池(InnoDB Buffer Pool),看這些數據頁是否已經存在於記憶體中。如果已經存在,則無需再次從磁碟載入。如果不存在,存儲引擎會將這些數據頁從磁碟載入到緩衝池中。

◦ 載入到緩衝池中的記錄可以被多個查詢共用,這有助於提高查詢效率。

  1. 執行器:處理連接、排序、聚合、過濾等操作。

◦ 在記憶體中執行連接操作,將employee表和employee_score表的數據行連接起來。

◦ 對連接後的結果集進行過濾,只保留滿足查詢條件(age > 18、subject_id = 3、score > 80)的數據行。

◦ 將過濾後的數據行作為查詢結果返回給客戶端。

前面說過,根據存儲引擎根據索引條件載入到記憶體的數據頁有多數據,可能有不滿足索引條件的數據,如果執行器不再次進行索引條件判斷, 則無法判斷哪些記錄滿足索引條件的,雖然在存儲引擎判斷過了,但是在執行器還是會有索引條件 age > 18、subject_id = 3、score > 80 的判斷。

我們再以全局視野來分析一下

  1. 確定驅動表: 首先,MySQL優化器會選擇一個表作為"驅動表"。通常,返回記錄數較少的表會被選為驅動表。假設employee_score表中滿足subject_id = 3 AND score > 80條件的記錄數量較少,那麼這張表可能被選為驅動表。這是優化器的工作,它預估哪個表作為驅動表更為高效,制定執行計劃。雖然驅動表的選擇很大程度上是基於預估的返回記錄數,但實際選擇還會受其他因素影響,例如表之間的連接類型、可用的索引等。
  2. 使用驅動表的索引進行篩選: 優化器會首先對驅動表進行篩選。如果employee_score是驅動表,優化器會使用subject_idscore的聯合索引來篩選出subject_id = 3 AND score > 80的記錄。這是執行器按照優化器的計劃向存儲引擎發出請求,獲取需要的數據。存儲引擎負責訪問索引,並根據索引定位到實際的數據頁,從而獲取數據行。
  3. 連接操作: 執行器會基於上一步從驅動表中篩選出的記錄對另一個表(即employee表)進行連接。這時,執行器會使用employee表上的索引(如id索引)來高效地找到匹配的記錄。
  4. 進一步的篩選: 在連接的過程中,執行器會考慮employee表的其他篩選條件,如age > 18,通常連接後才過濾篩選,這也是執行器的工作,執行器在連接過程中或之後,根據優化器制定的計划進一步篩選結果集。但是這裡employee表的age索引其葉子節點包含age和主鍵id信息,在進行連接時,可以直接按照age範圍掃描該索引,利用其葉子節點中的id信息進行高效的JOIN操作,因此在連接時就完成篩選,這個過程由MySQL優化器自動完成。從上面可以看到,當存在可以被利用的索引時,MySQL可以在連接過程中執行這些過濾操作。
  5. 返回結果: 這是執行器最後的步驟,返回最終的查詢結果。

四、總結

本文采用一張簡單的架構圖說明瞭MySQL查詢中使用的組件和組件間關係。

解析了一條sql語句從客戶端請求mysql伺服器到返回給客戶端的整個生命周期流程。

列舉了單表sql、關聯表sql 兩種不同SQL在整個生命周期中的執行順序和及內部組件邏輯關係。

通過如上案例的解析可以讓開發者們掌握到單表sql、關聯表sql的底層sql知識,為理解慢sql的產生和優化鑒定基礎。

作者:京東物流 高峰

來源:京東雲開發者社區 自猿其說Tech 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Nexus 是支持 Nuget、Docker、Npm 等多種包的倉庫管理器,可用做私有包的存儲分發,緩存官方包。本篇將手把手教學使用 Nexus 搭建自己的 NuGe t& Docker 私有倉庫。 ...
  • 有的時候我們會對程式進行單元測試, 為了測試的效果以及後期的維護, 我一般會將各個測試拆開, 根據需要測試的類分到各個類型中, 不過在實際操作的時候就出現了一些意想不到的問題, 各個測試的執行是亂序的, 按照我自己寫測試的習慣, 假如我需要測試新寫的增刪改查的功能, 我會將增刪改查分開測試, 會按照 ...
  • 接上篇 docker-bind 的使用搭建了一個 dns 服務,本篇將介紹另外一款 DnsServer 的部署和使用,更專註,更輕量。 ...
  • MongoDB+SignalR+Hangfire+Vue2+百度地圖實現GPS實時定位 一、實現效果 二、安裝MongoDB 可以自行參考菜鳥鏈接:MongoDB 教程 | 菜鳥教程 (runoob.com) 1.下載mongodb資料庫安裝包: 網盤鏈接:https://pan.baidu.com ...
  • 目錄String簡單介紹常見命令應用場景Hash簡單介紹常見命令應用場景List簡單介紹常見命令應用場景Set簡單介紹常見命令應用場景Sorted Set(Zset)簡單介紹常見命令應用場景Bitmap簡單介紹常見命令應用場景附錄 Redis支持多種數據類型,比如String、hash、list、S ...
  • 在構建數據倉庫或做數據分析時,需要對原始數據的結構進行一定的處理,有時涉及到“行轉列”,有時涉及到“列轉行”,那麼這兩個轉換的方式具體是什麼,有什麼差異,怎麼實現。 ...
  • 本文主要以介紹方法為主,落地過程可以歸納為方案->收益測算->數據安全驗證->系統穩定性驗證->灰度與回滾。文中的賬單系統通過step1大表壓縮32%,step2大JSON欄位序列化12%,step3刪除無效數據10%,3個方案的順利落地,有效的減少了50.7%的磁碟空間,成本下降也非常顯著。最後,... ...
  • 本文分享自華為雲社區《GaussDB(DWS)性能調優:表掃描時過濾行數過多引起的性能瓶頸問題案例》,作者: O泡果奶~ 。 1、【問題描述】 SQL語句執行過程中,對12億數據量的大表進行掃描,過濾99%的數據僅留617行數據,性能瓶頸位於掃描該表這裡。 2、【原始語句】 set search_p ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...