MySql執(zhí)行流程與生命周期詳解
我們經(jīng)常會(huì)在一起討論怎樣優(yōu)化 SQL、怎樣優(yōu)化數(shù)據(jù)庫(kù),那么請(qǐng)問(wèn)你知道執(zhí)行一條 SQL 的過(guò)程中都經(jīng)過(guò)了哪些動(dòng)作嗎?如果不知道的話,又怎么去優(yōu)化 SQL、優(yōu)化數(shù)據(jù)庫(kù)呢?為了更好地優(yōu)化 MySQL 數(shù)據(jù)庫(kù),接下來(lái)我們就來(lái)討論一下執(zhí)行一條 SQL 都經(jīng)過(guò)哪些步驟。
為了方便理解,在介紹 SQL 執(zhí)行流程之前,我們先通過(guò)一張圖,來(lái)詳細(xì)闡述一下 SQL 執(zhí)行流程。
由上圖,可以得出:在 MySQL 中,我們大致可以分為三個(gè)部分,分別是MySQL 客戶端、MySQL 服務(wù)端和存儲(chǔ)層。其中,存儲(chǔ)層主要是硬件層面,不在今天討論的范圍內(nèi),所以這里我們主要討論前兩者。
一、MySQL 客戶端
我們應(yīng)該都聽(tīng)說(shuō)過(guò) MySQL 數(shù)據(jù)庫(kù)支持很多編程語(yǔ)言的 API 接口,其實(shí)這句話底層的含義是很多編程語(yǔ)言是內(nèi)置 MySQL 客戶端。除此之外,MySQL 還有一個(gè)常用的客戶端就是 MySQL 數(shù)據(jù)庫(kù)自帶的一個(gè) mysql 命令,這個(gè)命令使用如下:
$ mysql -u$username -p$password -h$host -P$port
備注:
- username 是用戶名
- password 是密碼
- host 是 MySQL 服務(wù)端地址
- port 是 MySQL 服務(wù)端端口
復(fù)制代碼
一條 SQL 執(zhí)行的第一步是由 MySQL 客戶端發(fā)送到 MySQL 服務(wù)端。在這個(gè)步驟中直接影響數(shù)據(jù)速度的是網(wǎng)絡(luò),所以,數(shù)據(jù)庫(kù)服務(wù)端和客戶端之間最好要有良好的網(wǎng)絡(luò)環(huán)境。不過(guò),網(wǎng)絡(luò)不屬于本次討論的重點(diǎn),這里不做過(guò)多的討論。我們重點(diǎn)討論一下 MySQL 中的各個(gè)模塊對(duì)于數(shù)據(jù)庫(kù)速度的影響。
嚴(yán)格來(lái)說(shuō),MySQL 連接池屬于 MySQL 服務(wù)端的模塊,因其跟 MySQL 客戶端關(guān)系密切,我將其拿到 MySQL 客戶端中來(lái)介紹。
MySQL 客戶端成功連接 MySQL 服務(wù)端之后,MySQL 服務(wù)端的連接池會(huì)對(duì)客戶端的連接進(jìn)行權(quán)限驗(yàn)證,當(dāng)權(quán)限驗(yàn)證通過(guò)之后,MySQL 服務(wù)端會(huì)將客戶端的鏈接記錄在服務(wù)端的連接池中,之后的各種操作將不再進(jìn)行權(quán)限認(rèn)證。
這里你肯定會(huì)有疑問(wèn):為什么 MySQL 要增加一個(gè)連接池模塊呢?直接連接 MySQL 服務(wù)端不是更簡(jiǎn)單且高效嗎?下面我們就來(lái)分析一下。
每一次操作數(shù)據(jù)庫(kù),都需要有一個(gè) MySQL 客戶端和服務(wù)端之間的鏈接,創(chuàng)建一個(gè)鏈接就會(huì)有一定的時(shí)間消耗;當(dāng)在高并發(fā)的情況下,每一次訪問(wèn)數(shù)據(jù)都會(huì)創(chuàng)建一個(gè)鏈接,這樣就會(huì)持續(xù)創(chuàng)建很多重復(fù)的數(shù)據(jù)庫(kù)鏈接,很沒(méi)有必要而且大量的創(chuàng)建鏈接可能會(huì)導(dǎo)致數(shù)據(jù)庫(kù)內(nèi)存溢出等問(wèn)題。
為了解決這個(gè)問(wèn)題,MySQL 數(shù)據(jù)庫(kù)就提供了一個(gè)鏈接池模塊,主要用來(lái)保存和安全校驗(yàn)連接過(guò)來(lái)的客戶端,當(dāng)一個(gè)客戶端連接成功之后,并不會(huì)立即釋放該連接,而是會(huì)將該鏈接保存在連接池中,這樣下一次同一個(gè)客戶端連接過(guò)來(lái)就不需要再重復(fù)創(chuàng)建連接和安全驗(yàn)證了,可以在很大程度上降低數(shù)據(jù)庫(kù)的資源消耗并且減少連接數(shù)據(jù)的延時(shí)。
二、MySQL 服務(wù)端
在 MySQL 服務(wù)端中,SQL 執(zhí)行過(guò)程中是需要經(jīng)過(guò)很多模塊的,其中比較重要的模塊是緩存、SQL 解析器、查詢優(yōu)化器以及 SQL 執(zhí)行器等模塊。下面我來(lái)詳細(xì)介紹一下。
1. 查詢緩存
在 MySQL 數(shù)據(jù)庫(kù)中,如果開(kāi)啟了緩存查詢,每一次的查詢都會(huì)在緩存器中以 KV 形式緩存一份。一條 SQL 在執(zhí)行過(guò)程中,如果命中了緩存,就會(huì)跳過(guò) SQL 解析器、查詢優(yōu)化器以及 SQL 執(zhí)行器,并且立即返回?cái)?shù)據(jù),這樣做的目的主要是提高數(shù)據(jù)庫(kù)的性能。
其實(shí) MySQL 數(shù)據(jù)庫(kù)是將緩存以哈希的形式保存在內(nèi)存中的一個(gè)引用表中,并且把本次查詢的 SQL、數(shù)據(jù)庫(kù)名稱以及協(xié)議的 hash 值作為 key,這樣做的主要目的是下一次同一個(gè)查詢過(guò)來(lái)之后可以直接命中查詢。
不過(guò)緩存并不是永恒不變的,也會(huì)失效,我總結(jié)了以下幾個(gè)緩存失效的情況:
- 該條緩存對(duì)應(yīng)的數(shù)據(jù)、數(shù)據(jù)表發(fā)生變化時(shí),緩存就會(huì)自動(dòng)失效;
- 查詢過(guò)程中有變化的數(shù)據(jù)時(shí),是不會(huì)創(chuàng)建緩存的,例如 now()。
所以,在使用 MySQL 數(shù)據(jù)庫(kù)查詢的時(shí)候,要盡量避免更改數(shù)據(jù)和使用有變化的數(shù)據(jù)。
那么,既然緩存能夠提高 MySQL 數(shù)據(jù)的性能,應(yīng)該怎么設(shè)置開(kāi)啟緩存呢?首先,我們可以使用SHOW VARIABLES LIKE '%query_cache%';
來(lái)查詢緩存的配置項(xiàng):
-- 查詢緩存配置項(xiàng) mysql> SHOW VARIABLES LIKE '%query_cache%'; +------------------------------+---------+ | Variable_name | Value | +------------------------------+---------+ | have_query_cache | YES | | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 1048576 | | query_cache_type | OFF | | query_cache_wlock_invalidate | OFF | +------------------------------+---------+ 6 rows in set (0.01 sec)
其中,query_cache_type有三個(gè)選項(xiàng),分別是:
- OFF(0):關(guān)閉緩存。
- ON(1):開(kāi)啟緩存。
- DEMAND(2):按需開(kāi)啟緩存,加上 SQL_CACHE 關(guān)鍵字才會(huì)緩存。
所以,要開(kāi)啟查詢緩存可以在 MySQL 數(shù)據(jù)庫(kù)的配置文件 my.cnf 中添加query_cache_type = 1
即可。如果需要使用 DEMAND 的話,就需要配置成query_cache_type = 2
。如果需要緩存的話,就需要增加SQL_CACHE
關(guān)鍵字,具體操作如下:
mysql> select SQL_CACHE * from info; +----+--------------+ | id | name | +----+--------------+ | 1 | 小仙女 | | 2 | 小帥哥 | | 3 | 鐵錘妹妹 | +----+--------------+ 3 rows in set, 1 warning (0.00 sec)
2. SQL 解析器
當(dāng)沒(méi)有命中緩存時(shí),這個(gè)時(shí)候 MySQL 數(shù)據(jù)庫(kù)就得去查詢數(shù)據(jù)了。在查詢之前必須解析客戶端發(fā)送過(guò)來(lái)的以一系列字符串和空格組成的 SQL,此時(shí)就必須用到 MySQL 數(shù)據(jù)庫(kù)中的另一個(gè)模塊:SQL 解析器。
SQL 解析器的主要功能是解析客戶端發(fā)送過(guò)來(lái)的 SQL,就比如匹配到 SQL 中是以 select 開(kāi)頭的,那就可以認(rèn)定其為查詢語(yǔ)句;以 insert 開(kāi)頭的 SQL,就可以認(rèn)定其為插入語(yǔ)句。但如果在判斷的過(guò)程中,出現(xiàn)了匹配不到的這種情況,就會(huì)報(bào)出ERROR 1064 (42000): You have an error in your SQL syntax;
的錯(cuò)誤。具體如下:
mysql> elect * from info; ERROR 1064 (42000): 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 'elect * from info' at line 1
一般情況下,具體的錯(cuò)誤信息都是在最后邊的單引號(hào)內(nèi),具體問(wèn)題具體對(duì)待。
3. 查詢優(yōu)化器
查詢優(yōu)化器,顧名思義就是優(yōu)化 SQL 的執(zhí)行效率,也就是說(shuō)查詢優(yōu)化器的具體功能是為了找到 SQL 的最佳執(zhí)行方案。
在 MySQL 數(shù)據(jù)庫(kù)中的查詢優(yōu)化器優(yōu)化 SQL 具體有兩個(gè)方面,分別是邏輯層面和物理層面。
物理層面主要是跟硬件有關(guān),很難通過(guò)邏輯去優(yōu)化,所以這里我們從邏輯層面說(shuō)明一下。
邏輯層面的優(yōu)化主要有命中索引優(yōu)化、順序優(yōu)化、排序優(yōu)化等。例如連表查詢,具體如下:
mysql> select * from province inner join city on city.fid = province.id where province.id = 1; +----+-----------+----+--------------+------+ | id | name | id | name | fid | +----+-----------+----+--------------+------+ | 1 | 上海市 | 1 | 徐匯區(qū) | 1 | | 1 | 上海市 | 2 | 浦東新區(qū) | 1 | +----+-----------+----+--------------+------+ 2 rows in set (0.00 sec)
上面的 SQL 連接 province 和 city 兩個(gè)數(shù)據(jù)表,在內(nèi)存中,有如下兩種情況。
- 第一種情況:首先查詢 province 表中 id 為 1 的數(shù)據(jù),然后再查詢 city 表中 fid 為 1 的數(shù)據(jù)。
- 第二種情況:首先查詢 city 表中的所有數(shù)據(jù),然后再判斷 city 表中的 fid 是否等于 1。
這兩種情況的結(jié)果是一樣的,這個(gè)時(shí)候查詢優(yōu)化器內(nèi)部通過(guò)算法的方式判斷哪個(gè)方案的效率更高,進(jìn)而選擇哪個(gè)方案。
4. SQL 執(zhí)行器
當(dāng)分析完 SQL 并且選擇合適的方案之后,就開(kāi)始執(zhí)行 SQL 了,執(zhí)行 SQL 就需要使用 MySQL 數(shù)據(jù)庫(kù)提供的 SQL 執(zhí)行器模塊。
SQL 執(zhí)行模塊首先會(huì)判斷當(dāng)前用戶是否對(duì)該表有相關(guān)的操作權(quán)限(如果命中了緩存,將會(huì)在返回緩存數(shù)據(jù)之前進(jìn)行權(quán)限認(rèn)證)。權(quán)限判斷通過(guò)之后才會(huì)調(diào)用存儲(chǔ)引擎去操作對(duì)應(yīng)的數(shù)據(jù)表,然后將操作的結(jié)果返回。
總結(jié)
在這篇文章中,我們按照 SQL 的執(zhí)行順序介紹了一條 SQL 從客戶端到返回?cái)?shù)據(jù)期間經(jīng)過(guò)的各個(gè)模塊。這里我簡(jiǎn)單將各個(gè)要點(diǎn)匯總一下。
- MySQL 客戶端主要是用來(lái)將 SQL 發(fā)送至服務(wù)端的一個(gè)模塊。
- 連接池主要是用來(lái)保存成功連接 MySQL 服務(wù)端的鏈接的,這樣做的好處是可以防止數(shù)據(jù)庫(kù)連接短時(shí)間內(nèi)不斷重復(fù)創(chuàng)建,減少了資源浪費(fèi),提高了數(shù)據(jù)庫(kù)的訪問(wèn)速度。
- 數(shù)據(jù)庫(kù)緩存模塊是將之前訪問(wèn)過(guò)的數(shù)據(jù)保存在內(nèi)存中,這樣做的好處是避免相同的一個(gè)任務(wù)重復(fù)執(zhí)行的問(wèn)題,可以提高數(shù)據(jù)庫(kù)速度,并且也可以降低數(shù)據(jù)庫(kù)資源消耗。
- SQL 解析器主要是用于解析 SQL 的,搞清楚這條 SQL 的具體目的——查詢還是更新數(shù)據(jù)。
- SQL 優(yōu)化器是通過(guò)內(nèi)部的算法選擇執(zhí)行這條 SQL 效率最高的方案。
- 搞清了 SQL 的具體操作,也選擇了最優(yōu)的執(zhí)行方案,最后就開(kāi)始執(zhí)行 SQL,SQL 執(zhí)行器的主要作用除了調(diào)用存儲(chǔ)引擎接口獲取數(shù)據(jù)之外,還有權(quán)限認(rèn)證的作用。
至此,一條 SQL 的生命周期就結(jié)束了。
在實(shí)際應(yīng)用中,我建議 SQL 緩存最好設(shè)置成按需開(kāi)啟(默認(rèn)是關(guān)閉狀態(tài)) ,原因是 MySQL 中的某一個(gè)表更新會(huì)導(dǎo)致與這個(gè)表相關(guān)的所有緩存全部失效,這樣更新數(shù)據(jù)庫(kù)時(shí)的開(kāi)銷就會(huì)額外增加很多。
到此這篇關(guān)于MySql執(zhí)行流程與生命周期詳解的文章就介紹到這了,更多相關(guān)MySql生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04MySQL實(shí)戰(zhàn)教程之Join語(yǔ)句執(zhí)行流程
這篇文章主要介紹了MySQL Join語(yǔ)句執(zhí)行流程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03MySQL 索引的優(yōu)缺點(diǎn)以及創(chuàng)建索引的準(zhǔn)則
這篇文章主要介紹了MySQL 索引的優(yōu)劣以及創(chuàng)建索引的準(zhǔn)則,幫助大家更好的理解和使用MySQL 索引,感興趣的朋友可以了解下2020-09-09MySQL 配置免密碼登錄的問(wèn)題記錄(mysql_config_editor Configurati
這篇文章主要介紹了MySQL 配置免密碼登錄的問(wèn)題記錄(mysql_config_editor Configuration),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08mysql本地登錄無(wú)法使用端口號(hào)登錄的解決方法
這篇文章主要介紹了mysql本地登錄無(wú)法使用端口號(hào)登錄的解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06