作爲一個程序員,最不能避免的就是與sql打交道,那麼,在我們平時寫的那麼多sql它們是怎麼執行,並給我們返回數據的?
比如最簡單的一個查詢:
select * from user where id=10;
sql簡單,但問題是你知道它是如何執行的嗎?
先從整體看一下mysql數據庫的執行構造:
1)server層:
所有跨存儲引擎的功能都在這一層,比如:存儲過程、觸發器、視圖等。
2)存儲引擎層
其架構模式是插件的形式,比如,innodb。
以上可以認爲是從宏觀上,查看了mysql各個部分的流程執行順序。
那麼從微觀上看的話,一條查詢語句它是怎麼獲取到數據的呢?
知識儲備:
一、數據頁
在操作系統的概念中,我們從磁盤讀取數據時,操作系統每次會讀取4KB的數據。也就是說,即使你讀取1KB的數據,操作系統也會返回4Kb的數據,先稱其爲頁。其目的就是爲了減少IO的交互。爲什麼它能做到這一點?
舉例說明一下:
假如我們磁盤中有10條數據,並且每一條數據都是1KB,並且每一條都有一個唯一的遞增的id,假如現在,我要讀取id=3的這一條數據,來看一下有什麼不同。
1)不使用頁
我們從id=1的開始讀取,當我們讀取到想要的數據時,我們需要讀取3次,進行了3次磁盤IO。
2)使用頁
我們從id=1開始讀取,然後讀取一次,不符合,但是系統使用了頁,返回了四條數據,其中就包含了id=3的這條數據。這就只進行了一次磁盤io,效率是不是提高了。
那麼接下來,回到mysql中。innodb中也有這個頁的概念,但是它的默認大小是16KB。其作用是一樣的,減少訪問磁盤的次數。
先看一下數據頁的結構,這只是簡化了的結構,其內部結構比這要複雜。只用於幫助理解。
數據在一頁中是以單鏈表的形式存儲的。而其中的slot,佔兩個字節,記錄着相對頁初始位置的偏移量。每一個slot可以 包含4到8條數據,有它輔助,可以實現二分查找,找到對應的數據頁後進行遍歷讀取數據。
擴展:
innodb引擎的邏輯存儲結構從小到大可以分爲:
行、頁、區、段、表空間。
其中區固定爲1M的大小。
二、Buffer Pool
bufferPool使用來做什麼的?
由上面我們知道,數據庫讀取的時候都會讀取整個頁到內存,那麼內存如何存儲這些頁,存放到那裏面?
這就要使用buffer pool了。那麼它做了什麼?
當mysql啓動時,BufferPool中就會被初始化爲上圖結構。
其中雙向鏈表爲free鏈表,記錄着空閒的數據頁,如果此時開始的sql語句開始訪問,那麼就會去磁盤加載數據頁到內存,將緩存頁填充,然後將其對應的free鏈表的位置刪除。
那如何知道查詢的數據頁在不在BufferPool中呢?
它其中還維護了一張hash表數據空額後,用“表空間號+數據頁號”作爲key,緩存頁的地址作爲value,這樣查詢一下就知道在緩存中存不存在這個數據頁了。
三、索引
innodb所以使用的是B+樹。
上圖是一個主鍵索引數。
有了以上的數據儲備,我們來看看開始的時候,一條查詢語句是如何獲取數據的。
1)通過索引,查詢到對應的數據。
2)遍歷雙向鏈表(頁是一個雙向鏈表),找到數據所對應的頁。
3)將數據對應的頁加載到BufferPool中。
4)獲取對應的數據信息返回。