自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

8張圖,5大組件!了解MySQL查詢語句執(zhí)行過程

數(shù)據(jù)庫 MySQL
相信廣大程序員朋友經(jīng)常使用MySQL數(shù)據(jù)庫作為書籍持久化的工具,我們最常使用的就是MySQL中的SQL語句,從客戶端向MySQL發(fā)出一條條指令,然后獲取返回的數(shù)據(jù)結(jié)果進(jìn)行后面的邏輯處理。

[[382073]]

本文轉(zhuǎn)載自微信公眾號「石杉的架構(gòu)筆記」,作者崔皓。轉(zhuǎn)載本文請聯(lián)系石杉的架構(gòu)筆記公眾號。  

 開篇

  • 相信廣大程序員朋友經(jīng)常使用MySQL數(shù)據(jù)庫作為書籍持久化的工具,我們最常使用的就是MySQL中的SQL語句,從客戶端向MySQL發(fā)出一條條指令,然后獲取返回的數(shù)據(jù)結(jié)果進(jìn)行后面的邏輯處理。盡管大家經(jīng)常使用SQL語句完成工作,你是否關(guān)注過其執(zhí)行的階段,利用了哪些技術(shù)完成?今天,就帶大家一起看看MySQL數(shù)據(jù)庫處理SQL請求的全過程。下面將會講述如下內(nèi)容:
  • 查詢請求在MySQL中的處理流程
  • MySQL 中處理SQL的組件介紹,包括:
    • 連接器
    • 查詢緩存
    • 分析器
    • 優(yōu)化器
    • 執(zhí)行器

查詢請求的執(zhí)行流程

眾所周知在MySQL數(shù)據(jù)庫應(yīng)用中查詢請求是使用最多的,假設(shè)我們輸入代碼段1 中的SQL,通過客戶端請求MySQL服務(wù)器,會得到一個包含user的結(jié)果集。但是,其中MySQL的處理過程我們并不了解,那么下面就讓我們一起看看在查詢請求前后MySQL服務(wù)端發(fā)生了些什么吧。

  1. Select * from user where userId=1 

代碼段1

如圖1 所示:

圖1 MySQL 查詢請求處理流程

整張圖由三部分組成,從上到下分別是客戶端(紫色)、MySQL Server層(綠色)、MySQL存儲引擎層(黃色)。

  • 客戶端不言而喻,主要負(fù)責(zé)與MySQL Server層建立連接,發(fā)送查詢請求以及接受響應(yīng)的結(jié)果集。
  • MySQL Server層,主要包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等。這些組件包含了MySQL的大部分主要功能,例如平時使用最多的存儲過程、觸發(fā)器、視圖都在這一層中。還有一個通用的日志模塊 bin log。
  • MySQL 存儲引擎層,主要負(fù)責(zé)數(shù)據(jù)的存儲和提取。其支持多個存儲引擎,例如:InnoDB、MyISAM等。常用的有InnoDB,它從MySQL 5.5.5版本開始成為了MySQL的默認(rèn)存儲引擎,重要的是InnoDB 引擎包含了自帶的日志模塊 redo log,這個在后面講述更新語句的時候會著重提到。

上面介紹了MySQL的組件結(jié)構(gòu),那么這里將其處理SQL語句的流程簡單梳理一遍,之后再對每個組件逐一進(jìn)行介紹。如圖2 所示,在圖1 的基礎(chǔ)上加上了流程處理的編號,順著編號來看看MySQL的各各組件是如何處理SQL查詢請求的。

1. 連接器:當(dāng)客戶端登陸MySQL的時候,對身份認(rèn)證和權(quán)限判斷。

2. 查詢緩存: 執(zhí)行查詢語句的時候,會先查詢緩存(MySQL 8.0 版本后移除)。

3. 分析器: 假設(shè)在沒有命中查詢緩存的情況下,SQL請求就會來到分析器。分析器負(fù)責(zé)明確SQL要完成的功能,以及檢查SQL的語法是否正確。

4. 優(yōu)化器:為SQL提供優(yōu)化執(zhí)行的方案。

5. 執(zhí)行器: 將語句分發(fā)到對應(yīng)的存儲引擎執(zhí)行,并返回數(shù)據(jù)。

MySQL組件定義

下面通過一張大圖將SQL執(zhí)行流程進(jìn)行了梳理,這里將對應(yīng)的組件進(jìn)行詳細(xì)介紹。

圖2 SQL 請求執(zhí)行流程

連接器

客戶端需要通過連接器訪問MySQL Server,連接器主要負(fù)責(zé)身份認(rèn)證和權(quán)限鑒別的工作。也就是負(fù)責(zé)用戶登錄數(shù)據(jù)庫的相關(guān)認(rèn)證操作,例如:校驗賬戶密碼,權(quán)限等。在用戶名密碼合法的前提下,會在權(quán)限表中查詢用戶對應(yīng)的權(quán)限,并且將該權(quán)限分配給用戶。在連接完成以后可以通過圖3看到連接狀態(tài),可以通過命令行“show processlist”生成圖3的查詢結(jié)果。其中“Command”列返回的內(nèi)容中,“Sleep”表示MySQL相同中對應(yīng)一個空閑連接。而“Query”表示正在查詢的連接。

圖3 連接狀態(tài)

上面提到了連接狀態(tài),這里將5種連接狀態(tài)整理為如下表格,方便大家參考。

Command

含義

sleep

線程正在等待客戶端發(fā)數(shù)據(jù)

query

連接線程正在執(zhí)行查詢

locked

線程正在等待表鎖的釋放

sorting result

線程正在對結(jié)果進(jìn)行排序

sending data

向請求端返回數(shù)據(jù)

MySQL將連接器中的連接分為長連接和短連接。

  • 長連接是指連接成功后,客戶端請求一直使用是同一個連接。
  • 短連接是指每次執(zhí)行完SQL請求的操作之后會斷開連接,如果再有SQL請求會重新建立連接。由于短連接會反復(fù)創(chuàng)建連接消耗相同資源,因此多數(shù)情況下會選擇長連接。但是為了保持長連接,會占用系統(tǒng)內(nèi)存,而這些被占用的內(nèi)存知道連接斷開以后才會釋放。這里提出了兩個解決方案:

1.定期斷開長連接,每隔一段時間或者執(zhí)行一個占用內(nèi)存的大查詢以后斷開連接,從而釋放內(nèi)存,當(dāng)查詢的時候再重新創(chuàng)建連接。

2.MySQL 5.7 或者更高的版本,通過執(zhí)行 mysql_reset_connection 來重新初始化連接。此過程不會重新建立連接,但是會釋放占用的內(nèi)存,將連接恢復(fù)到剛剛創(chuàng)立連接的狀態(tài)。

查詢緩存

在建立與數(shù)據(jù)庫的連接以后就可以執(zhí)行SQL語句來,不過在執(zhí)行之前會先查詢緩存,其目的是查看是否之前執(zhí)行過該語句,并且將執(zhí)行結(jié)果按照key-value的形式緩存在內(nèi)存中了。

Key 是查詢的SQL語句,Value 是查詢的結(jié)果。如果緩存 Key 被命中,就會直接返回給客戶端,如果沒有命中,就會執(zhí)行后續(xù)的操作,執(zhí)行完SQL仍舊會把結(jié)果緩存起來,方便下一次調(diào)用。

MySQL 查詢不建議使用緩存,因為會出現(xiàn)這樣的場景:如果針對某張表進(jìn)行更新,針對這張表的查詢緩存就會被清空。如果張表不斷地被使用(更新、查詢),那么查詢緩存會頻繁地失效,獲取查詢緩存也失去了意義。不過可以運(yùn)用在一些修改不頻繁的數(shù)據(jù)表,例如:系統(tǒng)配置、或者修改不頻繁的表。緩存的淘汰策略是先進(jìn)先出,適用于查詢遠(yuǎn)大于修改的情況下, 否則建議使用Redis或者其他做緩存工具。因此大多數(shù)情況下不推薦使用查詢緩存。MySQL 8.0 版本后刪除了查詢緩存的功能,官方認(rèn)為該功能應(yīng)用場景較少,所以將其刪除。

如果你不需要在MySQL中使用查詢緩存,也可以將參數(shù)query_cache_type設(shè)置成 DEMAND,那么默認(rèn)情況下的執(zhí)行SQL語句時就不會使用查詢緩存了。如果打開了緩存可以通過“show status like 'Qcache%'”命令查看緩存的情況。

如圖4 緩存狀態(tài)

如圖4 所示,其中幾個使用較多的狀態(tài)值如下:

Qcache_inserts 是否有新的數(shù)據(jù)添加,每有一條數(shù)據(jù)添加Value會加一。

Qcache_hits 查詢語句是否命中緩存,每有一條語句命中Value會加一。

Qcache_free_memory 緩存空閑大小。

分析器

如果查詢緩存沒有命中,那么SQL請求會進(jìn)入分析器,分析器是用來分辨SQL語句的執(zhí)行目的,其執(zhí)行過程大致分為兩步:

第一步,詞法分析(Lexical scanner),主要負(fù)責(zé)從SQL 語句中提取關(guān)鍵字,比如:查詢的表,字段名,查詢條件等等。

第二步,語法規(guī)則(Grammar rule module),主要判斷SQL語句是否合乎MySQL的語法。

其實(shí)說白了詞法分析(Lexical scanner) 就是將整個SQL語句拆分成一個個單詞,而語法規(guī)則(Grammar rule module)則根據(jù)MySQL定義的語法規(guī)則生成對應(yīng)的數(shù)據(jù)結(jié)構(gòu),并存儲在對象結(jié)構(gòu)當(dāng)中。

其結(jié)果供優(yōu)化器生成執(zhí)行計劃,再調(diào)用存儲引擎接口執(zhí)行。來看下面這個例子,假設(shè)有這樣一個SQL語句“select username from userinfo”。

先通過詞法分析,從左到右逐個字符進(jìn)行解析,獲得如表1的四個單詞。

關(guān)鍵字

非關(guān)鍵字

關(guān)鍵字

非關(guān)鍵字

select

username

from

userinfo

表1 語法分析關(guān)鍵字

然后再通過語法規(guī)則解析,判斷輸入的SQL 語句是否滿足MySQL語法,并且生成圖5的語法樹。由SQL語句生成的四個單詞中,識別出兩個關(guān)鍵字,分別是select 和from。根據(jù)MySQL的語法Select 和 from之間對應(yīng)的是fields 字段,下面應(yīng)該掛接username;在from后面跟隨的是Tables字段,其下掛接的是userinfo。

圖5 語法規(guī)則生成語法樹

優(yōu)化器

優(yōu)化器的作用是對SQL進(jìn)行優(yōu)化,生成最有的執(zhí)行方案。如圖6所示,前面提到的SQL解析器通過語法分析和語法規(guī)則生成了SQL語法樹。這個語法樹作為優(yōu)化器的輸入,而優(yōu)化器(黃色的部分)包含了邏輯變換和代價優(yōu)化兩部分的內(nèi)容。在優(yōu)化完成以后會生成SQL執(zhí)行計劃作為整個優(yōu)化過程的輸出,交給執(zhí)行器在存儲引擎上執(zhí)行。

圖6 優(yōu)化器所處的位置

如上圖所示,這節(jié)的重點(diǎn)在優(yōu)化器中的邏輯變換和代價優(yōu)化上。

邏輯變換

邏輯變換也就是在關(guān)系代數(shù)基礎(chǔ)上進(jìn)行變換,其目的是為了化簡,同時保證SQL變化前后的結(jié)果一致,也就是邏輯變化并不會帶來結(jié)果集的變化。其主要包括以下幾個方面:

  • 否定消除:針對表達(dá)式“和取”或“析取”前面出現(xiàn)“否定”的情況,應(yīng)將關(guān)系條件進(jìn)行拆分,從而將外層的“NOT”消除。
  • 等值常量傳遞:利用了等值關(guān)系的傳遞特性,為了能夠盡早執(zhí)行“下推”運(yùn)算。“下推”的基本策略是,始終將過濾表達(dá)式盡可能移至靠近數(shù)據(jù)源的位置。
  • 常量表達(dá)式計算:對于能立刻計算出結(jié)果的表達(dá)式,直接計算結(jié)果,同時將結(jié)果與其他條件盡量提前進(jìn)行化簡。

這樣講概念或許有些抽象,通過圖7 來看看邏輯變化如何在SQL中執(zhí)行的吧。

圖7 邏輯變換

如圖7所示,從上往下共有4個步驟:

1.針對存在的SQL語句,首先通過“否定消除”,去掉條件判斷中的“NOT”。語句由原來的“or”轉(zhuǎn)換成“and”,并且大于小于符號進(jìn)行變號。藍(lán)色部分為修改前的SQL,紅色是修改以后的SQL。

2.等值傳遞,這一步很好理解分別降”t2.a=9” 和”t2.b=5”分別替換掉SQL中對應(yīng)的值。

3.接下來就是常量表達(dá)式計算,將“5+7”計算得到“12”。

4.最后是常量表達(dá)式計算后的化簡,將”9<=10”化簡為”true”帶入到最終的SQL表達(dá)式中完成優(yōu)化。

代價優(yōu)化

代價優(yōu)化是用來確定每個表,根據(jù)條件是否應(yīng)用索引,應(yīng)用哪個索引和確定多表連接的順序等問題。為了完成代價優(yōu)化,需要找到一個代價最小的方案。

因此,優(yōu)化器是通過基于代價的計算方法來決定如何執(zhí)行查詢的(Cost-based Optimization)。

簡化的過程如下:

  1. 賦值操作代價:針對每個數(shù)據(jù)庫操作(創(chuàng)建表、返回數(shù)據(jù)集)設(shè)置對應(yīng)的代價,這個代價值一般設(shè)置為1、0.2之類的值,沒有具體的含義就是對操作的代價定義。
  2. 計算操作數(shù)量:將SQL語句中涉及到的操作進(jìn)行邏輯,并且做計算。說白了就是看這次SQL請求需要做哪些具體的數(shù)據(jù)庫操作。
  3. 求和操作代價:既然知道SQL由哪些數(shù)據(jù)庫操作組成,同時知道每個操作對應(yīng)的代價,求和以后就是知道整體SQL執(zhí)行的代價。
  4. 選擇代價計劃:如果說沒給SQL執(zhí)行的操作都是一個計劃,那么這些操作的不同組合就會對應(yīng)不同的計劃,這里需要選擇整體執(zhí)行代價最低的操作計劃,作為這次執(zhí)行SQL語句的代價計劃,從而達(dá)到總代價最低。

這里將配置操作的代價分為MySQL 服務(wù)層和MySQL 引擎層,MySQL 服務(wù)層主要是定義CPU的代價,而MySQL 引擎層主要定義IO代價。MySQL 5.7 引入了兩個系統(tǒng)表mysql.server_cost和mysql.engine_cost來分別配置這兩個層的代價,如下:

  • MySQL 服務(wù)層代價保存在表server_cost中,其具體內(nèi)容如下:
  • row_evaluate_cost (default 0.2) 計算符合條件的行的代價,行數(shù)越多,此項代價越大
  • memory_temptable_create_cost (default 2.0) 內(nèi)存臨時表的創(chuàng)建代價
  • memory_temptable_row_cost (default 0.2) 內(nèi)存臨時表的行代價
  • key_compare_cost (default 0.1) 鍵比較的代價,例如排序
  • disk_temptable_create_cost (default 40.0) 內(nèi)部myisam或innodb臨時表的創(chuàng)建代價

disk_temptable_row_cost (default 1.0) 內(nèi)部myisam或innodb臨時表的行代價

  • 由上可以看出創(chuàng)建臨時表的代價是很高的,尤其是內(nèi)部的myisam或innodb臨時表。
  • MySQL 引擎層代價保存在表engine_cost中,其具體內(nèi)容如下:

io_block_read_cost (default 1.0) 從磁盤讀數(shù)據(jù)的代價,對innodb來說,表示從磁盤讀一個page的代價

memory_block_read_cost (default 1.0) 從內(nèi)存讀數(shù)據(jù)的代價,對innodb來說,表示從buffer pool讀一個page的代價

目前io_block_read_cost和memory_block_read_cost默認(rèn)值均為1,實(shí)際生產(chǎn)中建議酌情調(diào)大memory_block_read_cost,特別是對普通硬盤的場景。

MySQL會根據(jù)SQL查詢生成的查詢計劃中對應(yīng)的操作從上面兩張代價表中查找對應(yīng)的代價值,并且進(jìn)行累加形成最終執(zhí)行SQL計劃的代價。再將多種可能的執(zhí)行計劃進(jìn)行比較,選取最小代價的計劃執(zhí)行。

執(zhí)行器

當(dāng)分析器生成查詢計劃,并且經(jīng)過優(yōu)化器以后,就到了執(zhí)行器。執(zhí)行器會選擇執(zhí)行計劃開始執(zhí)行,但在執(zhí)行之前會校驗請求用戶是否擁有查詢的權(quán)限,如果沒有權(quán)限,就會返回錯誤信息,否則將會去調(diào)用MySQL引擎層的接口,執(zhí)行對應(yīng)的SQL語句并且返回結(jié)果。

例如SQL:“SELECT * FROM userinfo WHERE username = 'Tom';“

假設(shè) “username“ 字段沒有設(shè)置索引,就會調(diào)用存儲引擎從第一條開始查,如果碰到了用戶名字是” Tom“, 就將結(jié)果集返回,沒有查找到就查看下一行,重復(fù)上一步的操作,直到讀完整個表或者找到對應(yīng)的記錄。

需要注意SQL語句的執(zhí)行順序并不是按照書寫順序來的,順序的定義會在分析器中做好,一般是按照如下順序:

圖8 SQL的執(zhí)行順序

總結(jié)

本文從MySQL中SQL語句的執(zhí)行過程作為切入點(diǎn),首先介紹了查詢請求的執(zhí)行流程,其中將MySQL的處理分為MySQL Server層和MySQL存儲引擎層。通過介紹SQL語句的流轉(zhuǎn),引出了后面要介紹的5大組件,他們分別是:連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器。

后面的內(nèi)容中對每個組件進(jìn)行了詳細(xì)的介紹。連接器,負(fù)責(zé)身份認(rèn)證和權(quán)限鑒別;查詢緩存,將查詢的結(jié)果集進(jìn)行緩存,提高查詢效率;分析器,對SQL語句執(zhí)行語法分析和語法規(guī)則,生成語法樹和執(zhí)行計劃;優(yōu)化器,包括邏輯變換和代價優(yōu)化;執(zhí)行器,在檢查用戶權(quán)限以后對數(shù)據(jù)進(jìn)行逐條查詢,整個過程遵守SQL語句的執(zhí)行順序。

 

責(zé)任編輯:武曉燕 來源: 石杉的架構(gòu)筆記
相關(guān)推薦

2021-03-04 09:50:23

MySQL查詢語句

2022-09-01 16:42:47

MySQL數(shù)據(jù)庫架構(gòu)

2021-03-08 09:52:55

架構(gòu)運(yùn)維技術(shù)

2021-07-04 22:27:42

存儲BookKeeper系統(tǒng)

2020-06-28 07:39:44

Kafka分布式消息

2015-07-13 10:23:23

Java圖解

2020-11-13 10:29:37

流程控制語句

2023-11-04 16:23:37

sql優(yōu)化臨時表

2021-05-07 17:11:19

負(fù)載均衡運(yùn)維服務(wù)

2010-10-27 14:27:13

oracle查詢語句日

2010-04-29 14:06:40

Oracle SQL

2014-07-22 10:56:45

互聯(lián)網(wǎng)印度

2012-10-09 09:25:52

CPU制造過程硅晶片

2018-12-13 09:27:31

后臺服務(wù)架構(gòu)

2020-01-02 09:14:23

Kubernetes內(nèi)部容器

2024-08-01 20:08:17

2010-10-21 11:44:55

SQL Server分

2021-03-16 11:33:23

數(shù)據(jù)分析

2019-11-06 09:30:35

SQL查詢語句數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號