[資料庫系列之MySQL] Mysql整體架構淺析一

一、引言

平時我們在做Java系統時,一般情況下都會連接到一個MySQL資料庫上去,執行各種增刪改查的語句。大部分的Java工程師對MySQL的了解和掌握程度,大致就停留在這麼一個階段:對MySQL可以建庫建表建索 引,然後就是執行增刪改查去更新和查詢裡面的數據!如下圖所示:

 

 

 

 

但是實際在使用MySQL的過程中,大家總會遇到這樣那樣的一些問題,比如死鎖異常、SQL性能太差、異常報錯,等等。大部分Java工程師在遇到MySQL資料庫的一些問題時,一般都會上網百度、Google,然後自己嘗試搗鼓著解決一下,最後解決了問題,自己 可能也沒搞明白裡面的原理。但是具體MySQL底層原理是什麼呢?如果作為一名架構師我想是有必要去深入了解一下的,接下來我們就一起學習吧。

 

二、MYSQL Server應用層架構

我們知道MYSQL 架構上分為應用層架構和存儲引擎層架構,我們先來學習應用層架構,首先我們看一張應用層架構的整體設計圖,讓我們先有一個整體的認知,然後我們在來逐一拆解。

 

 

 

上圖告訴我們,我們的系統應用訪問我們的MYSQL進行增刪改查的操作並不是簡單的直接訪問了SQL,是經歷了很多步驟,比如SQL的解析,SQL的優化、SQL的執行等等才完成SQL的邏輯操作,下面我們來逐個講解內容。

 

三、應用端資料庫連接池

    為什麼在應用端會增加資料庫連接池,而不是我們上面說的直接發送請求連接到資料庫端?首先,我們知道如果要在Java系統中去訪問一個MySQL資料庫,必須得在系統的依賴中加入一個MySQL驅動,有了這個MySQL驅動才能跟MySQL資料庫建立連接,然後執行各種各樣的SQL語句,因為這個MySQL驅動,他會在底層跟資料庫建立網路連接,有網路連接,接著才能去發送請求給資料庫伺服器。然後當我們跟資料庫之間有了網路連接之後,我們的Java程式碼才能基於這個連接去執行各種各樣的增刪改查SQL語句。問題來了,我們上圖可以看到用戶的訪問是多個的,Tomcat也是多個執行緒來對接的,如果說每一個執行緒過來了之後,我們都使用MYSQL驅動的一個連接來應對肯定是不行的,因為會競爭這一個MYSQL連接那性能可想而知,那我們就要多個連接了,但是多個連接,如果來一個我們就創建一個連接,然後使用完成之後銷毀它,好像沒有什麼問題,可以解決了我們應對過來的多個連接問題。

    如果Tomcat中的每個執行緒在每次訪問資料庫的時候,都基於MySQL驅動去創建一個資料庫連接,然後執行SQL語句,然後執行完 之後再銷毀這個資料庫連接,這樣行不行呢?可能Tomcat中上百個執行緒會並發的頻繁創建資料庫連接,執行SQL語句,然後頻繁的銷毀資料庫連接。上述這個過程反覆循環執行,大家覺得可行嗎?這也是非常不好的,因為每次建立一個資料庫連接都很耗時,好不容易建立好了連接,執行完了SQL語句,你還把資料庫連接給銷毀了,下一次再重新建立資料庫連接,那肯定是效率很低下的!

    所以一般我們必須要使用一個資料庫連接池,也就是說在一個池子里維持多個資料庫連接,讓多個執行緒使用裡面的不同的資料庫連接去執行SQL語句,然後執行完SQL語句之後,不要銷毀這個資料庫連接,而是把連接放回池子里,後續還可以繼續使用。基於這樣的一個資料庫連接池的機制,就可以解決多個執行緒並發的使用多個資料庫連接去執行SQL語句的問題,而且還避免了資料庫連接使用完之後就銷毀的問題。

    常見的資料庫連接池有DBCP,C3P0,Druid,等等,大家如果有興趣的話,可以去搜索一下資料庫連接池的使用例子和程式碼,甚至探索一下資料庫連接池的底層原理。

 

四、MYSQL端資料庫連接池

現在我們已經知道,我們任何一個系統都會有一個資料庫連接池去訪問資料庫,也就是說這個系統會有多個資料庫連接,供多執行緒並發的使用。同時我們可能會有多個系統同時去訪問一個資料庫,這都是有可能的。

肯定會有很多系統要與MySQL資料庫建立很多個連接,那麼MySQL也必然要維護與系統之間的多個連接,所以MySQL架構體系中的第一個環節,就是連接池。實際上MySQL中的連接池就是維護了與系統之間的多個資料庫連接。除此之外,你的系統每次跟MySQL建立連接的 時候,還會根據你傳遞過來的帳號和密碼,進行帳號密碼的驗證,庫表許可權的驗證。

 

五、SQL介面:負責處理接收到的SQL語句

    當我們的系統只要能從資料庫連接池獲取到一個資料庫連接之後,我們就可以執行增刪改查的SQL語句了,但是現在假設我們的資料庫伺服器的連接池中的某個連接接收到了網路請求,假設就是一條SQL語句,那麼大家先思考一個問題,誰負責從這個連接中去監聽網路請求?誰負責從網路連接里把請求數據讀取出來?

    那就是網路連接必須得分配給一個執行緒去進行處理,由一個執行緒來監聽請求以及讀取請求數據,比如從網路連接中讀取和解析出來一條我們的系統發送過去的SQL語句。當MySQL內部的工作執行緒從一個網路連接中讀取出來一個SQL語句之後,此時會如何來執行這個SQL語句呢?如果你要去執行這個SQL語句,去完成底層數據的增刪改查,那這就是一項極度複雜的任務了!

    所以MySQL內部首先提供了一個組件,就是SQL介面(SQL Interface),他是一套執行SQL語句的介面,專門用於執行我們發送給MySQL的那些增刪改查的SQL語句因此MySQL的工作執行緒接收到SQL語句之後,就會轉交給SQL介面去執行。

 

六、查詢解析器:讓MySQL能看懂SQL語句

    問題:SQL介面怎麼執行SQL語句呢?你直接把SQL語句交給MySQL,他能看懂和理解這些SQL語句嗎?

    SQL語句,我們用人腦是直接就可以處理一下,只要懂SQL語法的人,立馬大家就知道他是什麼意思,但是MySQL自己本身也是一個系統,是一個資料庫管理系統,他沒法直接理解這些SQL語句!所以此時有一個關鍵的組件要出場了,那就是查詢解析器。所謂的SQL解析,就是按照既定的SQL語法,對我們按照SQL語法規則編寫的SQL語句進行解析,然後理解這個SQL語句要幹什麼事情。

 

七、查詢優化器:選擇最優的查詢路徑

    當我們通過解析器理解了SQL語句要幹什麼之後,接著會找查詢優化器(Optimizer)來選擇一個最優的查詢路徑。查詢優化器他會針對你編寫的幾十行、幾百行甚至上千行的複雜SQL語句生成查詢路徑樹,然後從裡面選擇一條最優的查詢路徑出來。相當於他會告訴你,你應該按照一個什麼樣的步驟和順序,去執行哪些操作,然後一步一步的把SQL語句就給完成了。

 

八、執行器:根據執行計劃調用存儲引擎的介面

    接下來的問題是優化的查詢路徑出來之後誰來負責調度的問題即和存儲引擎打交道的問題,那麼就是執行器了,執行器產生執行計劃調度存儲引擎。

    執行器會根據優化器選擇的執行方案,去調用存儲引擎的介面按照一定的順序和 步驟,就把SQL語句的邏輯給執行了。

 

九、調用存儲引擎介面,真正執行SQL語句

    查詢優化器選擇的最優查詢路徑,也就是你到底應該按照一個什麼樣的順序和步驟去執行這個SQL語句的路徑,由執行器生成執行計劃交給底層的存儲引擎去真正的執行。這個存儲引擎是MySQL的架構設計中很有特色的一個環節。

    真正在執行SQL語句的時候,要不然是更新數據,要不然是查詢數據,那麼數據你覺得存放在哪裡?

    資料庫自己就是一個程式語言寫出來的系統而已,然後啟動之後也是一個進程,執行他裡面的各種程式碼,也就是我們上面所說 的那些東西。所以對資料庫而言,我們的數據要不然是放在記憶體里,要不然是放在磁碟文件里,沒什麼特殊的地方!

    我們已經知道一個SQL語句要如何執行了,但是我們現在怎麼知道哪些數據在記憶體里?哪些數據在磁碟 里?我們執行的時候是更新記憶體的數據?還是更新磁碟的數據?我們如果更新磁碟的數據,是先查詢哪個磁碟文件,再更新哪 個磁碟文件?

    所以這個時候就需要存儲引擎了,存儲引擎其實就是執行SQL語句的,他會按照一定的步驟去查詢記憶體快取數據,更新磁碟數 據,查詢磁碟數據,等等,執行諸如此類的一系列的操作。

    MySQL的架構設計中,SQL介面、SQL解析器、查詢優化器其實都是通用的,他就是一套組件而已

    但是存儲引擎的話,他是支援各種各樣的存儲引擎的,比如我們常見的InnoDB、MyISAM、Memory等等,我們是可以選擇 使用哪種存儲引擎來負責具體的SQL語句執行的。

 

Tags: