
一、Oracle 中的 SQL 是如何執(zhí)行的
SQL 在 Oracle 中的執(zhí)行過程

從上面這張圖中可以看出,SQL 語句在 Oracle 中經(jīng)歷了以下的幾個步驟
1、語法檢查:檢查 SQL 拼寫是否正確,如果不正確,Oracle 會報語法錯誤。
2、語義檢查:檢查 SQL 中的訪問對象是否存在。比如我們在寫 SELECT 語句的時候,列名寫錯了,系統(tǒng)就會提示錯誤。語法檢查和語義檢查的作用是保證 SQL 語句沒有錯誤。
3、權(quán)限檢查:看用戶是否具備訪問該數(shù)據(jù)的權(quán)限。
4、共享池檢查:共享池(Shared Pool)是一塊內(nèi)存池,最主要的作用是緩存 SQL 語句和該語句的執(zhí)行計劃。Oracle 通過檢查共享池是否存在 SQL 語句的執(zhí)行計劃,來判斷進行軟解析,還是硬解析。那軟解析和硬解析又該怎么理解呢?
在共享池中,Oracle 首先對 SQL 語句進行 Hash 運算,然后根據(jù) Hash 值在庫緩存(Library Cache)中查找,如果存在 SQL 語句的執(zhí)行計劃,就直接拿來執(zhí)行,直接進入“執(zhí)行器”的環(huán)節(jié),這就是軟解析。
如果沒有找到 SQL 語句和執(zhí)行計劃,Oracle 就需要創(chuàng)建解析樹進行解析,生成執(zhí)行計劃,進入“優(yōu)化器”這個步驟,這就是硬解析。
共享池是 Oracle 中的術語,包括了庫緩存,數(shù)據(jù)字典緩沖區(qū)等。我們上面已經(jīng)講到了庫緩存區(qū),它主要緩存 SQL 語句和執(zhí)行計劃。而數(shù)據(jù)字典緩沖區(qū)存儲的是 Oracle 中的對象定義,比如表、視圖、索引等對象。當對 SQL 語句進行解析的時候,如果需要相關的數(shù)據(jù),會從數(shù)據(jù)字典緩沖區(qū)中提取。
5、優(yōu)化器:優(yōu)化器中就是要進行硬解析,也就是決定怎么做,比如創(chuàng)建解析樹,生成執(zhí)行計劃。
6、執(zhí)行器:當有了解析樹和執(zhí)行計劃之后,就知道了 SQL 該怎么被執(zhí)行,這樣就可以在執(zhí)行器中執(zhí)行語句了。
二、MySQL 中的 SQL 是如何執(zhí)行的
首先 MySQL 是典型的C/S 架構(gòu),即 Client/Server 架構(gòu),服務器端程序使用的 mysqld。整體的 MySQL 流程如下圖所示:

MySQL 由三層組成:
- 連接層:客戶端和服務器端建立連接,客戶端發(fā)送 SQL 至服務器端;
- SQL 層:對 SQL 語句進行查詢處理;
- 存儲引擎層:與數(shù)據(jù)庫文件打交道,負責數(shù)據(jù)的存儲和讀取。
SQL 層的結(jié)構(gòu):

- 查詢緩存:Server 如果在查詢緩存中發(fā)現(xiàn)了這條 SQL 語句,就會直接將結(jié)果返回給客戶端;如果沒有,就進入到解析器階段。需要說明的是,因為查詢緩存往往效率不高,所以在 MySQL8.0 之后就拋棄了這個功能。
- 解析器:在解析器中對 SQL 語句進行語法分析、語義分析。
- 優(yōu)化器:在優(yōu)化器中會確定 SQL 語句的執(zhí)行路徑,比如是根據(jù)全表檢索,還是根據(jù)索引來檢索等。
- 執(zhí)行器:在執(zhí)行之前需要判斷該用戶是否具備權(quán)限,如果具備權(quán)限就執(zhí)行 SQL 查詢并返回結(jié)果。在 MySQL8.0 以下的版本,如果設置了查詢緩存,這時會將查詢結(jié)果進行緩存。
你能看到 SQL 語句在 MySQL 中的流程是:SQL 語句→緩存查詢→解析器→優(yōu)化器→執(zhí)行器。在一部分中,MySQL 和 Oracle 執(zhí)行 SQL 的原理是一樣的。
與 Oracle 不同的是,MySQL 的存儲引擎采用了插件的形式,每個存儲引擎都面向一種特定的數(shù)據(jù)庫應用環(huán)境。同時開源的 MySQL 還允許開發(fā)人員設置自己的存儲引擎,下面是一些常見的存儲引擎:
- 1.InnoDB 存儲引擎:它是 MySQL 5.5 版本之后默認的存儲引擎,最大的特點是支持事務、行級鎖定、外鍵約束等。
- MyISAM 存儲引擎:在 MySQL 5.5 版本之前是默認的存儲引擎,不支持事務,也不支持外鍵,最大的特點是速度快,占用資源少。
- Memory 存儲引擎:使用系統(tǒng)內(nèi)存作為存儲介質(zhì),以便得到更快的響應速度。不過如果 mysqld 進程崩潰,則會導致所有的數(shù)據(jù)丟失,因此我們只有當數(shù)據(jù)是臨時的情況下才使用 Memory 存儲引擎。
- NDB 存儲引擎:也叫做 NDB Cluster 存儲引擎,主要用于 MySQL Cluster 分布式集群環(huán)境,類似于 Oracle 的 RAC 集群。
- Archive 存儲引擎:它有很好的壓縮機制,用于文件歸檔,在請求寫入時會進行壓縮,所以也經(jīng)常用來做倉庫。
需要注意的是,數(shù)據(jù)庫的設計在于表的設計,而在 MySQL 中每個表的設計都可以采用不同的存儲引擎,我們可以根據(jù)實際的數(shù)據(jù)處理需要來選擇存儲引擎,這也是 MySQL 的強大之處。
三、如何在 MySQL 中對一條 SQL 語句的執(zhí)行時間進行分析
首先我們需要看下 profiling 是否開啟,開啟它可以讓 MySQL 收集在 SQL 執(zhí)行時所使用的資源情況,命令如下:
select @@profiling;

profiling=0 代表關閉,我profiling=1 代表開啟:
set profiling=1;
然后我們執(zhí)行一個 SQL 查詢
select * from course;
show profiles;

如果我們想要獲取上一次查詢的執(zhí)行時間,可以使用:
show profile;

