我們在日常的工作中會經(jīng)常接觸到數(shù)據(jù)庫,那么這些數(shù)據(jù)庫的SQL語句都是怎么執(zhí)行的呢?
這次我著重從以下幾個方面進(jìn)行介紹:
- Oracle中的SQL是如何執(zhí)行的,什么是硬解析和軟解析
- MySQL中的SQL是如何執(zhí)行的,MySQL的體系結(jié)構(gòu)是怎樣的
- 什么是存儲引擎,MySQL的存儲引擎有哪些
Oracle中的SQL是如何執(zhí)行的
先來看下SQL在Oracle中的執(zhí)行過程:
從上面這張圖中可以看出,SQL 語句在 Oracle 中經(jīng)歷了如下過程:
語法檢查:檢查 SQL 拼寫是否正確,如果不正確,Oracle 會報語法錯誤。
語義檢查:檢查 SQL 中的訪問對象是否存在。比如我們在寫 SELECT 語句的時候,列名寫錯了,系統(tǒng)就會提示錯誤。語法檢查和語義檢查的作用是保證 SQL 語句沒有錯誤。
權(quán)限檢查:看用戶是否具備訪問該數(shù)據(jù)的權(quán)限。
共享池檢查:共享池(Shared Pool)是一塊內(nèi)存池,最主要的作用是緩存 SQL 語句和該語句的執(zhí)行計(jì)劃。Oracle 通過檢查共享池是否存在 SQL 語句的執(zhí)行計(jì)劃,來判斷進(jìn)行軟解析,還是硬解析。那軟解析和硬解析又該怎么理解呢?
在共享池中,Oracle 首先對 SQL 語句進(jìn)行 Hash 運(yùn)算,然后根據(jù) Hash 值在庫緩存(Library Cache)中查找,如果存在 SQL 語句的執(zhí)行計(jì)劃,就直接拿來執(zhí)行,直接進(jìn)入“執(zhí)行器”的環(huán)節(jié),這就是軟解析。
如果沒有找到 SQL 語句和執(zhí)行計(jì)劃,Oracle 就需要創(chuàng)建解析樹進(jìn)行解析,生成執(zhí)行計(jì)劃,進(jìn)入“優(yōu)化器”這個步驟,這就是硬解析。
優(yōu)化器:優(yōu)化器中就是要進(jìn)行硬解析,也就是決定怎么做,比如創(chuàng)建解析樹,生成執(zhí)行計(jì)劃。
執(zhí)行器:當(dāng)有了解析樹和執(zhí)行計(jì)劃之后,就知道了 SQL 該怎么被執(zhí)行,這樣就可以在執(zhí)行器中執(zhí)行語句了。
共享池是Oracle中的術(shù)語,包括庫緩存,數(shù)據(jù)字典緩沖區(qū)等。上面說的就是庫緩存區(qū),主要緩存SQL語句和執(zhí)行計(jì)劃。而數(shù)據(jù)字典緩沖區(qū)存儲的是Oracle中的對象定義,比如表,視圖,索引等對象。當(dāng)對SQL語句進(jìn)行解析的時候,如果需要相關(guān)數(shù)據(jù),就會從數(shù)據(jù)字典緩沖區(qū)提取。
庫緩存這一個步驟,決定了SQL語句是否需要進(jìn)行硬解析。為了提升SQL的執(zhí)行效率,應(yīng)該盡量避免硬解析,因?yàn)樵赟QL的執(zhí)行過程中,創(chuàng)建解析樹,生成執(zhí)行計(jì)劃是十分消耗資源的。
你可能會問,如何避免硬解析,盡量使用軟解析呢?在Oracle中,綁定變量是它的一個特色。具體來說,就是在SQL語句中使用變量,通過不同的變量取值來改變SQL的執(zhí)行結(jié)果。這樣的好處是能夠提升軟解析的可能性,不足之處就在于可能會導(dǎo)致生成的執(zhí)行計(jì)劃不夠優(yōu)化,因此是否需要綁定變量還需要視情況而定。
舉一個例子,我們使用下面的查詢語句:
SQL > select * from player where player_id = 10001;
如果使用綁定變量,如:
SQL > select * from player where player_id = :player_id;
這兩個查詢語句的效率在Oracle中完全不同,如果在查詢player_id = 10001以后,還會查詢10002、10003之類數(shù)據(jù),那么每一次查詢都會創(chuàng)建一個新的查詢解析。而第二種方式使用了綁定變量,那么在第一次查詢之后,在共享池中就會存在這類查詢的執(zhí)行計(jì)劃,也就是軟解析。
MySQL中的SQL是如何執(zhí)行的
MySQL是典型的C/S架構(gòu),服務(wù)端使用的mysqld。
可以看到,MySQL由三層組成:
連接層:客戶端和服務(wù)器端建立連接,客戶端發(fā)送SQL到服務(wù)端。
SQL層:對SQL語句進(jìn)行查詢處理。
-
存儲引擎層:與數(shù)據(jù)庫文件打交道,負(fù)責(zé)數(shù)據(jù)讀取和存儲。
查詢緩存:Server如果在查詢緩存時發(fā)現(xiàn)了這條SQL語句,就會直接將結(jié)果返回給客戶端;如果沒有,機(jī)會進(jìn)入到解析器階段。需要說明的情況是,因?yàn)椴樵兙彺娴男什皇呛芨撸虼嗽贛ySQL8.0之后就逐漸拋棄了這個功能。
解析器:在解析器中對SQL語句進(jìn)行語法分析,語義分析。
優(yōu)化器:在優(yōu)化器中會確定SQL語句的執(zhí)行路徑,比如是 根據(jù)全表進(jìn)行檢索,還是根據(jù)索引進(jìn)行檢索。
執(zhí)行器:在執(zhí)行之前應(yīng)該判斷該用戶是否具備了權(quán)限,具備權(quán)限就執(zhí)行SQL查詢并返回結(jié)果。
所以在MySQL中的SQL語句執(zhí)行流程是:SQL語句->緩存查詢->解析器->優(yōu)化器->執(zhí)行器。
與Oracle不同的是,MySQL的存儲引擎采用了插件的形式,每個存儲引擎都面向一種特定的數(shù)據(jù)庫應(yīng)用環(huán)境。下面是一些常用的存儲引擎:
- InnoDB 存儲引擎:它是 MySQL 5.5.8 版本之后默認(rèn)的存儲引擎,最大的特點(diǎn)是支持事務(wù)、行級鎖定、外鍵約束等。
- MyISAM 存儲引擎:在 MySQL 5.5.8 版本之前是默認(rèn)的存儲引擎,不支持事務(wù),也不支持外鍵,最大的特點(diǎn)是速度快,占用資源少。
- Memory 存儲引擎:使用系統(tǒng)內(nèi)存作為存儲介質(zhì),以便得到更快的響應(yīng)速度。不過如果 mysqld 進(jìn)程崩潰,則會導(dǎo)致所有的數(shù)據(jù)丟失,因此我們只有當(dāng)數(shù)據(jù)是臨時的情況下才使用 Memory 存儲引擎。
- NDB 存儲引擎:也叫做 NDB Cluster 存儲引擎,主要用于 MySQL Cluster 分布式集群環(huán)境,類似于 Oracle 的 RAC 集群。
- Archive 存儲引擎:它有很好的壓縮機(jī)制,用于文件歸檔,在請求寫入時會進(jìn)行壓縮,所以也經(jīng)常用來做倉庫。
總結(jié)
兩種數(shù)據(jù)庫都是通過解析器->優(yōu)化器->執(zhí)行器
這樣的流程來執(zhí)行SQL的。
但是Oracle和MySQL在進(jìn)行SQL的查詢上面有軟件實(shí)現(xiàn)層面的差異。Oracle提出了共享池的概念,通過共享池來判斷是進(jìn)行軟解析還是硬解析。而在MySQL中,8.0以后的版本不再支持查詢緩存,而是直接執(zhí)行解析器->優(yōu)化器->執(zhí)行器的流程,這一點(diǎn)從MySQL中的show profile里也能看到。