久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL中SQL執(zhí)行流程是怎么樣的

154次閱讀
沒有評論

共計 4264 個字符,預(yù)計需要花費 11 分鐘才能閱讀完成。

這篇文章主要介紹 MySQL 中 SQL 執(zhí)行流程是怎么樣的,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

MYSQL 體系結(jié)構(gòu)

先看一張架構(gòu)圖,如下:

模塊詳解

Connector:用來支持各種語言和 SQL 的交互,比如 PHP,Python,Java 的 JDBC;

Management Serveices Utilities:系統(tǒng)管理和控制工具,包括備份恢復(fù)、MySQL 復(fù)制、集群等;

Connection Pool:連接池,管理需要緩沖的資源,包括用戶密碼權(quán)限線程等等;

SQL Interface:用來接收用戶的 SQL 命令,返回用戶需要的查詢結(jié)果;

Parser:用來解析 SQL 語句;

Optimizer:查詢優(yōu)化器;

Cache and Buffer:查詢緩存,除了行記錄的緩存之外,還有表緩存,Key 緩存,權(quán)限緩存等等;

Pluggable Storage Engines:插件式存儲引擎,它提供 API 給服務(wù)層使用,跟具體的文件打交道。

架構(gòu)分層

把 MySQL 分成三層,跟客戶端對接的連接層,真正執(zhí)行操作的服務(wù)層,和跟硬件打交道的存儲引擎層。

連接層

我們的客戶端要連接到 MySQL 服務(wù)器 3306 端口,必須要跟服務(wù)端建立連接,那么管理所有的連接,驗證客戶端的身份和權(quán)限,這些功能就在連接層完成。

服務(wù)層

連接層會把 SQL 語句交給服務(wù)層,這里面又包含一系列的流程:

比如查詢緩存的判斷、根據(jù) SQL 調(diào)用相應(yīng)的接口,對我們的 SQL 語句進(jìn)行詞法和語法的解析(比如關(guān)鍵字怎么識別,別名怎么識別,語法有沒有錯誤等等)。

然后就是優(yōu)化器,MySQL 底層會根據(jù)一定的規(guī)則對我們的 SQL 語句進(jìn)行優(yōu)化,最后再交給執(zhí)行器去執(zhí)行。

存儲引擎

存儲引擎就是我們的數(shù)據(jù)真正存放的地方,在 MySQL 里面支持不同的存儲引擎。再往下就是內(nèi)存或者磁盤。

SQL 的執(zhí)行流程

以一條查詢語句為例,我們來看下 MySQL 的工作流程是什么樣的。

select name from user where id=1 and age

首先咱們先來看一張圖,接下來的過程都是基于這張圖來講的:

連接

程序或者工具要操作數(shù)據(jù)庫,第一步要跟數(shù)據(jù)庫建立連接。

在數(shù)據(jù)庫中有兩種連接:

短連接:短連接就是操作完畢以后,馬上 close 掉。

長連接:長連接可以保持打開,減少服務(wù)端創(chuàng)建和釋放連接的消耗,后面的程序訪問的時候還可以使用這個連接。

建立連接是比較麻煩的,首先要發(fā)送請求,發(fā)送了請求要去驗證賬號密碼,驗證完了要去看你所擁有的權(quán)限,所以在使用過程中,盡量使用長連接。

保持長連接會消耗內(nèi)存。長時間不活動的連接,MySQL 服務(wù)器會斷開。可以使用 sql 語句查看默認(rèn)時間:

show global variables like  wait_timeout

這個時間是由 wait_timeout 來控制的,默認(rèn)都是 28800 秒,8 小時。

查詢緩存

MySQL 內(nèi)部自帶了一個緩存模塊。執(zhí)行相同的查詢之后我們發(fā)現(xiàn)緩存沒有生效,為什么?MySQL 的緩存默認(rèn)是關(guān)閉的。

show variables like  query_cache%

默認(rèn)關(guān)閉的意思就是不推薦使用,為什么 MySQL 不推薦使用它自帶的緩存呢?

主要是因為 MySQL 自帶的緩存的應(yīng)用場景有限:

第一個是它要求 SQL 語句必須一模一樣,中間多一個空格,字母大小寫不同都被認(rèn)為是不同的的 SQL。

第二個是表里面任何一條數(shù)據(jù)發(fā)生變化的時候,這張表所有緩存都會失效,所以對于有大量數(shù)據(jù)更新的應(yīng)用,也不適合。

所以緩存還是交給 ORM 框架(比如 MyBatis 默認(rèn)開啟了一級緩存),或者獨立的緩存服務(wù),比如 Redis 來處理更合適。

在 MySQL 8.0 中,查詢緩存已經(jīng)被移除了。

語法解析和預(yù)處理

為什么一條 SQL 語句能夠被識別呢?假如隨便執(zhí)行一個字符串 hello,服務(wù)器報了一個 1064 的錯:

[Err] 1064 – You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near hello at line 1

這個就是 MySQL 的解析器和預(yù)處理模塊。

這一步主要做的事情是對語句基于 SQL 語法進(jìn)行詞法和語法分析和語義的解析。

詞法解析

詞法分析就是把一個完整的 SQL 語句打碎成一個個的單詞。

比如一個簡單的 SQL 語句:select name from user where id = 1 and age

它會將 select 識別出來,這是一個查詢語句,接下來會將 user 也識別出來,你是想要在這個表中做查詢,然后將 where 后面的條件也識別出來,原來我需要去查找這些內(nèi)容。

語法分析

語法分析會對 SQL 做一些語法檢查,比如單引號有沒有閉合,然后根據(jù) MySQL 定義的語法規(guī)則,根據(jù) SQL 語句生成一個數(shù)據(jù)結(jié)構(gòu)。這個數(shù)據(jù)結(jié)構(gòu)我們把它叫做解析樹(select_lex)。

就比如英語里面的語法“我用 is,你用 are”這種,如果不對肯定是不可以的,語法分析之后發(fā)現(xiàn)你的 SQL 語句不符合規(guī)則,就會收到 You hava an error in your SQL syntax 的錯誤提示。

預(yù)處理器

如果寫了一個詞法和語法都正確的 SQL,但是表名或者字段不存在,會在哪里報錯?是在數(shù)據(jù)庫的執(zhí)行層還是解析器?比如:select * from hello;

還是在解析的時候報錯,解析 SQL 的環(huán)節(jié)里面有個預(yù)處理器。它會檢查生成的解析樹,解決解析器無法解析的語義。比如,它會檢查表和列名是否存在,檢查名字和別名,保證沒有歧義。預(yù)處理之后得到一個新的解析樹。

查詢優(yōu)化器

一條 SQL 語句是不是只有一種執(zhí)行方式?或者說數(shù)據(jù)庫最終執(zhí)行的 SQL 是不是就是我們發(fā)送的 SQL?

這個答案是否定的。一條 SQL 語句是可以有很多種執(zhí)行方式的,最終返回相同的結(jié)果,他們是等價的。但是如果有這么多種執(zhí)行方式,這些執(zhí)行方式怎么得到的?最終選擇哪一種去執(zhí)行?根據(jù)什么判斷標(biāo)準(zhǔn)去選擇?

這個就是 MySQL 的查詢優(yōu)化器的模塊(Optimizer)。查詢優(yōu)化器的目的就是根據(jù)解析樹生成不同的執(zhí)行計劃(Execution Plan),然后選 擇一種最優(yōu)的執(zhí)行計劃,MySQL 里面使用的是基于開銷(cost)的優(yōu)化器,那種執(zhí)行計劃開銷最小,就用哪種。

可以使用這個命令查看查詢的開銷:

show status like  Last_query_cost

MySQL 的優(yōu)化器能處理哪些優(yōu)化類型呢?

舉兩個簡單的例子:

1、當(dāng)我們對多張表進(jìn)行關(guān)聯(lián)查詢的時候,以哪個表的數(shù)據(jù)作為基準(zhǔn)表。

2、有多個索引可以使用的時候,選擇哪個索引。

實際上,對于每一種數(shù)據(jù)庫來說,優(yōu)化器的模塊都是必不可少的,他們通過復(fù)雜的算法實現(xiàn)盡可能優(yōu)化查詢效率的目標(biāo)。但是優(yōu)化器也不是萬能的,并不是再垃圾的 SQL 語句都能自動優(yōu)化,也不是每次都能選擇到最優(yōu)的執(zhí)行計劃,大家在編寫 SQL 語句的時候還是要注意。

執(zhí)行計劃

優(yōu)化器最終會把解析樹變成一個執(zhí)行計劃(execution_plans),執(zhí)行計劃是一個數(shù)據(jù)結(jié)構(gòu)。當(dāng)然,這個執(zhí)行計劃不一定是最優(yōu)的執(zhí)行計劃,因為 MySQL 也有可能覆蓋不到所有的執(zhí)行計劃。

我們怎么查看 MySQL 的執(zhí)行計劃呢?比如多張表關(guān)聯(lián)查詢,先查詢哪張表?在執(zhí)行查詢的時候可能用到哪些索引,實際上用到了什么索引?

MySQL 提供了一個執(zhí)行計劃的工具。我們在 SQL 語句前面加上 EXPLAIN,就可以看到執(zhí)行計劃的信息。

EXPLAIN select name from user where id=1;

存儲引擎

在介紹存儲引擎先來問兩個問題:

1、從邏輯的角度來說,我們的數(shù)據(jù)是放在哪里的,或者說放在一個什么結(jié)構(gòu)里面?

2、執(zhí)行計劃在哪里執(zhí)行?是誰去執(zhí)行?

存儲引擎基本介紹

在關(guān)系型數(shù)據(jù)庫里面,數(shù)據(jù)是放在表 Table 里面的。我們可以把這個表理解成 Excel 電子表格的形式。所以我們的表在存儲數(shù)據(jù)的同時,還要組織數(shù)據(jù)的存儲結(jié)構(gòu),這個存儲結(jié)構(gòu)就是由我們的存儲引擎決定的,所以我們也可以把存儲引擎叫做表類型。

在 MySQL 里面,支持多種存儲引擎,他們是可以替換的,所以叫做插件式的存儲引擎。為什么要支持這么多存儲引擎呢?一種還不夠用嗎?

在 MySQL 里面,每一張表都可以指定它的存儲引擎,而不是一個數(shù)據(jù)庫只能使用一個存儲引擎。存儲引擎的使用是以表為單位的。而且,創(chuàng)建表之后還可以修改存儲引擎。

如何選擇存儲引擎?

如果對數(shù)據(jù)一致性要求比較高,需要事務(wù)支持,可以選擇 InnoDB。

如果數(shù)據(jù)查詢多更新少,對查詢性能要求比較高,可以選擇 MyISAM。

如果需要一個用于查詢的臨時表,可以選擇 Memory。

如果所有的存儲引擎都不能滿足你的需求,并且技術(shù)能力足夠,可以根據(jù)官網(wǎng)內(nèi)部手冊用 C 語言開發(fā)一個存儲引擎。(https://dev.mysql.com/doc/internals/en/custom-engine.html%EF%BC%89)

執(zhí)行引擎

誰使用執(zhí)行計劃去操作存儲引擎呢?這就是執(zhí)行引擎(執(zhí)行器),它利用存儲引擎提供的相應(yīng)的 API 來完成操作。

為什么我們修改了表的存儲引擎,操作方式不需要做任何改變?因為不同功能的存儲引擎實現(xiàn)的 API 是相同的。

最后把數(shù)據(jù)返回給客戶端,即使沒有結(jié)果也要返回。

栗子

還是以上面的 sql 語句為例,再來梳理一下整個 sql 執(zhí)行流程。

select name from user where id = 1 and age 

通過連接器查詢當(dāng)前執(zhí)行者的角色是否有權(quán)限,進(jìn)行查詢。如果有的話,就繼續(xù)往下走,如果沒有的話,就會被拒絕掉,同時報出 Access denied for user 的錯誤信息;

接下來就是去查詢緩存,首先看緩存里面有沒有,如果有呢,那就沒有必要向下走,直接返回給客戶端結(jié)果就可以了;如果緩存中沒有的話,那就去執(zhí)行語法解析器和預(yù)處理模塊。(MySQL 8.0 版本直接將查詢緩存的整塊功能都給刪掉了)

語法解析器和預(yù)處理主要是分析 sql 語句的詞法和語法是否正確,沒啥問題就會進(jìn)行下一步,來到查詢優(yōu)化器;

查詢優(yōu)化器就會對 sql 語句進(jìn)行一些優(yōu)化,看哪種方式是最節(jié)省開銷,就會執(zhí)行哪種 sql 語句,上面的 sql 有兩種優(yōu)化方案:

先查詢表 user 中 id 為 1 的人的姓名,然后再從里面找年齡大于 20 歲的。

先查詢表 user 中年齡大于 20 歲的所有人,然后再從里面找 id 為 1 的。

優(yōu)化器決定選擇哪個方案之后,執(zhí)行引擎就去執(zhí)行了。然后返回給客戶端結(jié)果。

以上是“MySQL 中 SQL 執(zhí)行流程是怎么樣的”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注丸趣 TV 行業(yè)資訊頻道!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-07-27發(fā)表,共計4264字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 和林格尔县| 民县| 通州区| 九龙坡区| 侯马市| 桦南县| 诸暨市| 萝北县| 阿拉尔市| 青铜峡市| 汉沽区| 昭平县| 高碑店市| 麟游县| 丹棱县| 沈阳市| 云浮市| 牡丹江市| 古蔺县| 苍南县| 乃东县| 手机| 定南县| 乐昌市| 元谋县| 久治县| 西盟| 永清县| 三河市| 社会| 南投市| 乳源| 大荔县| 康定县| 崇明县| 忻州市| 普安县| 东平县| 宁国市| 富宁县| 福州市|