Mysql中select查詢語句的執(zhí)行方式
MySQL 查詢語句可以分為 Server 層和存儲(chǔ)引擎層。而 Server 層包括連接器、查詢緩存、解析器、預(yù)處理器、優(yōu)化器、執(zhí)行器等,
最后 Server 層再通過 API 接口形式調(diào)用對(duì)應(yīng)的存儲(chǔ)引擎層提供的接口來執(zhí)行增刪改查操作。
如下圖所示:
?
1、介紹
關(guān)于sql語句在執(zhí)行過程中,可能會(huì)涉及到以下的組件來進(jìn)行相互的調(diào)用。
1.1、組件介紹
組件及其作用,可參考:
1.2、Sql執(zhí)行順序
在 SQL 中,select語句的語法結(jié)構(gòu)看似是按照關(guān)鍵字書寫順序執(zhí)行的(如select ...
from ...
where ...
),但實(shí)際執(zhí)行順序與語法順序并不完全一致。
如下圖所示:
注意:
在包含子查詢的 SQL 中,子查詢本身也是一個(gè)完整的select語句,其內(nèi)部仍然遵循上述執(zhí)行順序。外部查詢的執(zhí)行順序會(huì)以子查詢的最終結(jié)果為輸入繼續(xù)執(zhí)行。
為了更好地理解這個(gè)順序,我們可以從“數(shù)據(jù)流”的角度思考:
FROM
:首先確定從哪個(gè)表或視圖中讀取數(shù)據(jù)。WHERE
:在獲取原始數(shù)據(jù)后,先進(jìn)行行級(jí)過濾,減少后續(xù)處理的數(shù)據(jù)量。GROUP BY
:將過濾后的數(shù)據(jù)按指定字段分組,為聚合操作做準(zhǔn)備。HAVING
:在分組后,進(jìn)一步過濾不滿足條件的組。SELECT
:確定最終返回的字段(包括常量、表達(dá)式、聚合函數(shù)等)。ORDER BY
:對(duì)最終結(jié)果進(jìn)行排序。LIMIT
:限制返回的記錄數(shù)量。
如下圖所示:
2、執(zhí)行流程
一個(gè)完整的執(zhí)行流程包括以下組件,連接器、緩存、解析器、優(yōu)化器、執(zhí)行器和存儲(chǔ)引擎組成。
如下圖所示:
2.1. 連接與認(rèn)證
MySQL 服務(wù)端和客戶端的通信方式采用的是半雙工協(xié)議。
客戶端連接:客戶端通過協(xié)議(如 TCP/IP、Socket)與 MySQL 服務(wù)端建立連接。
認(rèn)證:服務(wù)端驗(yàn)證用戶身份(用戶名、密碼、權(quán)限)。如果驗(yàn)證失敗,連接終止。
權(quán)限檢查:確認(rèn)用戶是否有權(quán)限執(zhí)行當(dāng)前查詢(如 SELECT 權(quán)限)。
常見的通信方式主要可以分為三種:單工,半雙工,全雙工。
單工:
通信的時(shí)候,數(shù)據(jù)只能單向傳輸。比如說遙控器,我們只能用遙控器來控制電視機(jī),而不能用電視機(jī)來控制遙控器。
半雙工:
通信的時(shí)候,數(shù)據(jù)可以雙向傳輸,但是同一時(shí)間只能有一臺(tái)服務(wù)器在發(fā)送數(shù)據(jù),當(dāng) A 給 B 發(fā)送數(shù)據(jù)的時(shí)候,那么 B 就不能給 A
發(fā)送數(shù)據(jù),必須等到 A 發(fā)送結(jié)束之后,B 才能給 A 發(fā)送數(shù)據(jù)。比如說對(duì)講機(jī)。
全雙工:
通信的時(shí)候,數(shù)據(jù)可以雙向傳輸,并且可以同時(shí)傳輸。比如說我們打電話或者用通信軟件進(jìn)行語音和視頻通話等。
半雙工協(xié)議讓 MySQL 通信簡單快速,但是也在一定程度上限制了 MySQL 的性能,因?yàn)橐坏囊欢碎_始發(fā)送數(shù)據(jù),另一端必須要接收完全部數(shù)據(jù)才能做出響應(yīng)。
所以當(dāng)批量插入的時(shí)候盡量拆分成多次插入而不要一次插入太大數(shù)據(jù),同樣的查詢語句最好也帶上 limit 限制條數(shù),避免一次返回過多數(shù)據(jù)。
MySQL 單次傳輸數(shù)據(jù)包的大小可以通過變量 max_allowed_packet 控制,默認(rèn)大小為 64MB(5.7 版本默認(rèn)只有 4MB)。
執(zhí)行以下語句查看 max_allowed_packet 變量大?。?/p>
2.2. 查詢緩存
緩存命中檢查:
如果開啟了查詢緩存(Query Cache),MySQL 會(huì)直接檢查是否有完全相同的查詢結(jié)果緩存。
命中:直接返回緩存結(jié)果。
未命中:進(jìn)入后續(xù)流程,并可能將結(jié)果寫入緩存(取決于配置)。
注意:MySQL 8.0 已移除查詢緩存功能。
移除原因:
因?yàn)?MySQL 的緩存使用條件非??量蹋峭ㄟ^一個(gè)大小寫敏感的哈希值去匹配的,這樣就是說一條查詢語句哪怕只是有一個(gè)空格不一致,都會(huì)導(dǎo)致無法使用緩存。而且一旦表里面有一行數(shù)據(jù)變動(dòng)了,那么關(guān)于這種表的所有緩存都會(huì)失效,所以一般我們都是不建議使用緩存。
在 MySQL 8.0 版本之前緩存也是默認(rèn)關(guān)閉的,可以通過變量 query_cache_type 進(jìn)行控制。
2.3. 語法解析(Parser)
詞法分析:將 SQL 語句拆分為 token(如關(guān)鍵字、標(biāo)識(shí)符、操作符等)。
語法分析:根據(jù) SQL 語法樹規(guī)則,檢查語句合法性(如SELECT * FROM table
是否符合語法)。
生成抽象語法樹(AST):將 SQL 轉(zhuǎn)換為數(shù)據(jù)庫可理解的內(nèi)部結(jié)構(gòu)。
整個(gè)sql語句會(huì)被分割成:select,name,from,table,where,id,=,1這幾個(gè)字符。并且能識(shí)別出關(guān)鍵字和非關(guān)鍵字,然后根據(jù) sql 語句生成一個(gè)數(shù)據(jù)結(jié)構(gòu),也叫做解析樹。
如下圖所示:
2.4、執(zhí)行sql
如下圖所示:
1. 預(yù)處理(Preprocessor)
語義檢查:
- 檢查表、列是否存在。
- 驗(yàn)證權(quán)限(如用戶是否有權(quán)限訪問指定的表或列)。
- 替換別名、處理函數(shù)等。
生成邏輯查詢計(jì)劃:
- 將 AST 轉(zhuǎn)換為邏輯執(zhí)行計(jì)劃(如select a from t1 where b = 5)。
2. 查詢優(yōu)化器(Optimizer)
其目標(biāo)是生成最優(yōu)的執(zhí)行計(jì)劃(即最小資源消耗、最快響應(yīng)時(shí)間)。
優(yōu)化步驟:
選擇訪問路徑:決定是否使用索引(如全表掃描 vs 索引掃描)。如果有多個(gè)索引,選擇最合適的索引。
連接順序優(yōu)化(針對(duì)多表查詢):決定表的連接順序(如 A JOIN B JOIN C 的順序)。使用動(dòng)態(tài)規(guī)劃或代價(jià)模型(Cost Model)計(jì)算最優(yōu)順序。
其他優(yōu)化:優(yōu)化子查詢(如轉(zhuǎn)換為 JOIN)。優(yōu)化排序(ORDER BY)和分組(GROUP BY)。常量傳播、條件簡化等。
輸出執(zhí)行計(jì)劃:生成物理執(zhí)行計(jì)劃(如使用哪個(gè)索引、連接算法等)。
3. 執(zhí)行器(Executor)
執(zhí)行查詢計(jì)劃:
- 調(diào)用存儲(chǔ)引擎接口(如 InnoDB、MyISAM)獲取數(shù)據(jù)。
- 根據(jù)執(zhí)行計(jì)劃逐層處理(如過濾、排序、分組)。
權(quán)限二次檢查:
- 確保用戶對(duì)訪問的數(shù)據(jù)有權(quán)限。
數(shù)據(jù)處理:
- 讀取數(shù)據(jù)行(通過全表掃描或索引掃描)。
- 應(yīng)用 WHERE 條件過濾。
- 對(duì)結(jié)果進(jìn)行排序、分組、聚合等操作。
2.5. 存儲(chǔ)引擎交互
存儲(chǔ)引擎接口:
- MySQL 通過統(tǒng)一的接口(Handler API)與存儲(chǔ)引擎交互。
InnoDB 的處理:
- 通過 B+ 樹索引定位數(shù)據(jù)。
- 處理事務(wù)(如鎖機(jī)制、MVCC)。
- 返回符合條件的記錄。
其他引擎:
- 如 MyISAM 的處理方式不同(無事務(wù)支持)。
2.6. 返回結(jié)果
結(jié)果集組裝:
- 將處理后的數(shù)據(jù)按用戶指定的格式(如 SELECT 字段)組裝。
分頁處理:
- 若涉及 LIMIT 或分頁,截取對(duì)應(yīng)的數(shù)據(jù)。
返回客戶端:
- 一次性返回(小結(jié)果集)。
- 流式返回(大結(jié)果集,通過網(wǎng)絡(luò)分批次傳輸)。
MySQL 將查詢結(jié)果返回是一個(gè)增量的逐步返回過程。
當(dāng)處理完所有查詢邏輯并開始執(zhí)行查詢并且生成第一條結(jié)果數(shù)據(jù)的時(shí)候,MySQL 就可以開始逐步的向客戶端傳輸數(shù)據(jù)了。這么做的好處是服務(wù)端無需存儲(chǔ)太多結(jié)果,從而減少內(nèi)存消耗。
3、示例流程(SELECT 查詢)
以SELECT * FROM users WHERE id = 1;
為例:
- 客戶端連接數(shù)據(jù)庫。
- 檢查查詢緩存(未命中)。
- 解析 SQL 語法,生成 AST。
- 預(yù)處理:檢查
users
表和id
列是否存在。 - 優(yōu)化器選擇
id
索引進(jìn)行查找。 - 執(zhí)行器調(diào)用 InnoDB 引擎,通過索引定位到
id=1
的記錄。 - 返回結(jié)果給客戶端。
4、常見問題與優(yōu)化
- 慢查詢:檢查執(zhí)行計(jì)劃(使用
EXPLAIN
),優(yōu)化索引或調(diào)整查詢邏輯。 - 全表掃描:添加合適的索引或優(yōu)化查詢條件。
- JOIN 性能:調(diào)整連接順序或使用索引。
- 緩存失效:頻繁更新的表可能導(dǎo)致緩存命中率低(MySQL 8.0 后不再依賴此)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解java調(diào)用ffmpeg轉(zhuǎn)換視頻格式為flv
這篇文章主要介紹了 詳解java調(diào)用ffmpeg轉(zhuǎn)換視頻格式為flv的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09MySQL中ALTER?LOGFILE?GROUP?語句的具體使用
ALTER LOGFILE GROUP語句用于修改重做日志組的配置,本文主要介紹了MySQL中ALTER?LOGFILE?GROUP?語句的具體使用,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07MySQL事務(wù)及Spring隔離級(jí)別實(shí)現(xiàn)原理詳解
這篇文章主要介紹了MySQL事務(wù)及Spring隔離級(jí)別實(shí)現(xiàn)原理詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05SQL中CONVERT轉(zhuǎn)換函數(shù)的簡單使用方法
CONVERT()函數(shù)對(duì)于簡單類型轉(zhuǎn)換,CONVERT()函數(shù)和CAST()函數(shù)的功能相同,只是語法不同,下面這篇文章主要給大家介紹了關(guān)于SQL中CONVERT轉(zhuǎn)換函數(shù)的簡單使用方法,需要的朋友可以參考下2024-01-01