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

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

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

  1. Oracle中的SQL是如何執行的,什麼是硬解析和軟解析;
  2. MySQL中的SQL是如何執行的,MySQL的體系結構又是怎樣的;
  3. 什麼是存儲引擎,MySQL的存儲引擎都有哪些?

Oracle中的SQL是如何執行的

我們先來看下SQL在Oracle中的執行過程:

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

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

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

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

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

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

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

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

  6. 執行器:當有了解析樹和執行計劃之後,就知道了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由三層組成:

  1. 連接層:客戶端和服務器端建立連接,客戶端發送SQL至服務器端;
  2. SQL層:對SQL語句進行查詢處理;
  3. 存儲引擎層:與數據庫文件打交道,負責數據的存儲和讀取。

其中SQL層與數據庫文件的存儲方式無關,我們來看下SQL層的結構:

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

你能看到SQL語句在MySQL中的流程是:SQL語句→緩存查詢→解析器→優化器→執行器。在一部分中,MySQL和Oracle執行SQL的原理是一樣的。

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

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

需要注意的是,數據庫的設計在於表的設計,而在MySQL中每個表的設計都可以採用不同的存儲引擎,我們可以根據實際的數據處理需要來選擇存儲引擎,這也是MySQL的強大之處。

數據庫管理系統也是一種軟件

我們剛纔瞭解了SQL語句在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的過程,歡迎你把它分享給你的朋友或者同事。

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