轉-學會用數據庫的方式思考 SQL 是如何執行的

轉自:https://www.infoq.cn/article/t-JqP7vrgPJkk9RFZWrz

雖然 SQL 是聲明式語言,我們可以像使用英語一樣使用它,不過在 RDBMS(關係型數據庫管理系統)中,SQL 的實現方式還是有差別的。今天我們就從數據庫的角度來思考一下 SQL 是如何被執行的。

關於今天的內容,你會從以下幾個方面進行學習:

Oracle 中的 SQL 是如何執行的,什麼是硬解析和軟解析;
MySQL 中的 SQL 是如何執行的,MySQL 的體系結構又是怎樣的;
什麼是存儲引擎,MySQL 的存儲引擎都有哪些?
Oracle 中的 SQL 是如何執行的
我們先來看下 SQL 在 Oracle 中的執行過程:

oracle執行過程

從上面這張圖中可以看出,SQL 語句在 Oracle 中經歷了以下的幾個步驟。

語法檢查:檢查 SQL 拼寫是否正確,如果不正確,Oracle 會報語法錯誤。

語義檢查:檢查 SQL 中的訪問對象是否存在。比如我們在寫 SELECT 語句的時候,列名寫錯了,系統就會提示錯誤。語法檢查和語義檢查的作用是保證 SQL 語句沒有錯誤。

權限檢查:看用戶是否具備訪問該數據的權限。

共享池檢查:共享池(Shared Pool)是一塊內存池,最主要的作用是緩存 SQL 語句和該語句的執行計劃。Oracle 通過檢查共享池是否存在 SQL 語句的執行計劃,來判斷進行軟解析,還是硬解析。那軟解析和硬解析又該怎麼理解呢?

在共享池中,Oracle 首先對 SQL 語句進行 Hash 運算,然後根據 Hash 值在庫緩存(Library Cache)中查找,如果存在 SQL 語句的執行計劃,就直接拿來執行,直接進入“執行器”的環節,這就是軟解析。

如果沒有找到 SQL 語句和執行計劃,Oracle 就需要創建解析樹進行解析,生成執行計劃,進入“優化器”這個步驟,這就是硬解析。

優化器:優化器中就是要進行硬解析,也就是決定怎麼做,比如創建解析樹,生成執行計劃。

執行器:當有了解析樹和執行計劃之後,就知道了 SQL 該怎麼被執行,這樣就可以在執行器中執行語句了。

共享池是 Oracle 中的術語,包括了庫緩存,數據字典緩衝區等。我們上面已經講到了庫緩存區,它主要緩存 SQL 語句和執行計劃。而數據字典緩衝區存儲的是 Oracle 中的對象定義,比如表、視圖、索引等對象。當對 SQL 語句進行解析的時候,如果需要相關的數據,會從數據字典緩衝區中提取。

庫緩存這一個步驟,決定了 SQL 語句是否需要進行硬解析。爲了提升 SQL 的執行效率,我們應該儘量避免硬解析,因爲在 SQL 的執行過程中,創建解析樹,生成執行計劃是很消耗資源的。

你可能會問,如何避免硬解析,儘量使用軟解析呢?在 Oracle 中,綁定變量是它的一大特色。綁定變量就是在 SQL 語句中使用變量,通過不同的變量取值來改變 SQL 的執行結果。這樣做的好處是能提升軟解析的可能性,不足之處在於可能會導致生成的執行計劃不夠優化,因此是否需要綁定變量還需要視情況而定。

舉個例子,我們可以使用下面的查詢語句:

SQL> select * from player where player_id = 10001;

你也可以使用綁定變量,如:

SQL> select * from player where player_id = :player_id;

這兩個查詢語句的效率在 Oracle 中是完全不同的。如果你在查詢 player_id = 10001 之後,還會查詢 10002、10003 之類的數據,那麼每一次查詢都會創建一個新的查詢解析。而第二種方式使用了綁定變量,那麼在第一次查詢之後,在共享池中就會存在這類查詢的執行計劃,也就是軟解析。

因此我們可以通過使用綁定變量來減少硬解析,減少 Oracle 的解析工作量。但是這種方式也有缺點,使用動態 SQL 的方式,因爲參數不同,會導致 SQL 的執行效率不同,同時 SQL 優化也會比較困難。

MySQL 中的 SQL 是如何執行的
Oracle 中採用了共享池來判斷 SQL 語句是否存在緩存和執行計劃,通過這一步驟我們可以知道應該採用硬解析還是軟解析。那麼在 MySQL 中,SQL 是如何被執行的呢?

首先 MySQL 是典型的 C/S 架構,即 Client/Server 架構,服務器端程序使用的 mysqld。整體的 MySQL 流程如下圖所示:
mysql執行過程

你能看到 MySQL 由三層組成:

連接層:客戶端和服務器端建立連接,客戶端發送 SQL 至服務器端;
SQL 層:對 SQL 語句進行查詢處理;
存儲引擎層:與數據庫文件打交道,負責數據的存儲和讀取。
其中 SQL 層與數據庫文件的存儲方式無關,我們來看下 SQL 層的結構:
mysql-sql層

查詢緩存:Server 如果在查詢緩存中發現了這條 SQL 語句,就會直接將結果返回給客戶端;如果沒有,就進入到解析器階段。需要說明的是,因爲查詢緩存往往效率不高,所以在 MySQL8.0 之後就拋棄了這個功能。
解析器:在解析器中對 SQL 語句進行語法分析、語義分析。
優化器:在優化器中會確定 SQL 語句的執行路徑,比如是根據全表檢索,還是根據索引來檢索等。
執行器:在執行之前需要判斷該用戶是否具備權限,如果具備權限就執行 SQL 查詢並返回結果。在 MySQL8.0 以下的版本,如果設置了查詢緩存,這時會將查詢結果進行緩存。
你能看到 SQL 語句在 MySQL 中的流程是:SQL 語句→緩存查詢→解析器→優化器→執行器。在一部分中,MySQL 和 Oracle 執行 SQL 的原理是一樣的。

與 Oracle 不同的是,MySQL 的存儲引擎採用了插件的形式,每個存儲引擎都面向一種特定的數據庫應用環境。同時開源的 MySQL 還允許開發人員設置自己的存儲引擎,下面是一些常見的存儲引擎:

InnoDB 存儲引擎:它是 MySQL 5.5 版本之後默認的存儲引擎,最大的特點是支持事務、行級鎖定、外鍵約束等。
MyISAM 存儲引擎:在 MySQL 5.5 版本之前是默認的存儲引擎,不支持事務,也不支持外鍵,最大的特點是速度快,佔用資源少。
Memory 存儲引擎:使用系統內存作爲存儲介質,以便得到更快的響應速度。不過如果 mysqld 進程崩潰,則會導致所有的數據丟失,因此我們只有當數據是臨時的情況下才使用 Memory 存儲引擎。
NDB 存儲引擎:也叫做 NDB Cluster 存儲引擎,主要用於 MySQL Cluster 分佈式集羣環境,類似於 Oracle 的 RAC 集羣。
Archive 存儲引擎:它有很好的壓縮機制,用於文件歸檔,在請求寫入時會進行壓縮,所以也經常用來做倉庫。
需要注意的是,數據庫的設計在於表的設計,而在 MySQL 中每個表的設計都可以採用不同的存儲引擎,我們可以根據實際的數據處理需要來選擇存儲引擎,這也是 MySQL 的強大之處。

數據庫管理系統也是一種軟件
我們剛纔瞭解了 SQL 語句在 Oracle 和 MySQL 中的執行流程,實際上完整的 Oracle 和 MySQL 結構圖要複雜得多:
完整oracle
完整mysql

如果你只是簡單地把 MySQL 和 Oracle 看成數據庫管理系統軟件,從外部看難免會覺得“晦澀難懂”,畢竟組織結構太多了。我們在學習的時候,還需要具備抽象的能力,抓取最核心的部分:SQL 的執行原理。因爲不同的 DBMS 的 SQL 的執行原理是相通的,只是在不同的軟件中,各有各的實現路徑。

既然一條 SQL 語句會經歷不同的模塊,那我們就來看下,在不同的模塊中,SQL 執行所使用的資源(時間)是怎樣的。下面我來教你如何在 MySQL 中對一條 SQL 語句的執行時間進行分析。

首先我們需要看下 profiling 是否開啓,開啓它可以讓 MySQL 收集在 SQL 執行時所使用的資源情況,命令如下:

mysql> select @@profiling;

profiling=0 代表關閉,我們需要把 profiling 打開,即設置爲 1:

mysql> set profiling=1;

然後我們執行一個 SQL 查詢(你可以執行任何一個 SQL 查詢):

mysql> select * from wucai.heros;

查看當前會話所產生的所有 profiles:
在這裏插入圖片描述
你會發現我們剛纔執行了兩次查詢,Query ID 分別爲 1 和 2。如果我們想要獲取上一次查詢的執行時間,可以使用:

mysql> show profile;

在這裏插入圖片描述

當然你也可以查詢指定的 Query ID,比如:

mysql> show profile for query 2;

查詢 SQL 的執行時間結果和上面是一樣的。

在 8.0 版本之後,MySQL 不再支持緩存的查詢,原因我在上文已經說過。一旦數據表有更新,緩存都將清空,因此只有數據表是靜態的時候,或者數據表很少發生變化時,使用緩存查詢才有價值,否則如果數據表經常更新,反而增加了 SQL 的查詢時間。

你可以使用 select version() 來查看 MySQL 的版本情況。
在這裏插入圖片描述

總結
我們在使用 SQL 的時候,往往只見樹木,不見森林,不會注意到它在各種數據庫軟件中是如何執行的,今天我們從全貌的角度來理解這個問題。你能看到不同的 RDBMS 之間有相同的地方,也有不同的地方。

相同的地方在於 Oracle 和 MySQL 都是通過解析器→優化器→執行器這樣的流程來執行 SQL 的。

但 Oracle 和 MySQL 在進行 SQL 的查詢上面有軟件實現層面的差異。Oracle 提出了共享池的概念,通過共享池來判斷是進行軟解析,還是硬解析。而在 MySQL 中,8.0 以後的版本不再支持查詢緩存,而是直接執行解析器→優化器→執行器的流程,這一點從 MySQL 中的 show profile 裏也能看到。同時 MySQL 的一大特色就是提供了各種存儲引擎以供選擇,不同的存儲引擎有各自的使用場景,我們可以針對每張表選擇適合的存儲引擎。
在這裏插入圖片描述

內容來自極客時間專欄《SQL 必知必會》,作者陳暘,清華大學計算機博士,如果這篇文章幫你理順了 Oracle 和 MySQL 執行 SQL 的過程,歡迎你把它分享給你的朋友或者同事。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章