Mysql-Innodb : 從一個字節到整個數據庫表了解物理存儲結構和邏輯存儲結構


首先要從Innodb怎么看待磁盤物理空間說起

     一塊原生的(Raw)物理磁盤,可以把他看成一個字節一個字節單元組成的物理存儲介質

  

    如果要在這塊原生物理空間中插入一條記錄,不能單單只插入數據,還需要插入一些管理記錄的信息,這些管理信息被稱為記錄頭,這里假設是5字節(compact類型記錄確實記錄頭占用5字節,簡單通俗起見,可以忽略這段括號內的解釋)

  

 

 

   然后在記錄頭后面插入列,假如要插入的記錄的各個列是:

   

 

 

    其中 num 是主鍵 (int類型)

    name 是 varchar 類型的

    sex 是 int 類型的

    那么按照 int 占用 4 字節,不超過長度閾值(8000+)的 varchar 按照實際長度 ('abc' 占 3 字節) 存儲的規則 把這條記錄填充到 Raw物理空間中

   

    問題來了:管理信息有什么用呢?

    在存儲組織上最重要的用處是找到下一條記錄

       

 

 

 

    不能直接找到下一條記錄嗎?不能。假如我已經知道了第一條記錄數據的開頭部分,也就是上圖第一個藍色方格(A)的編號

    現在插入多一條記錄:

     

        如何才能獲取第二條記錄的第一個藍色方格(B)編號? 這個藍色方格(B)就是第二條記錄數據部分的起始地址

   可不可以用 A 的編號加上偏移量得到呢? 

     

 

  B的編號 = A 的編號 + 8 + 6  ? 實際上不行,因為黃色部分的 name 是不定長的,偏移量也不一定,就如下圖,偏移量隨着不定長字段長度改變而改變

 

    

 

 

   管理信息可以記錄下一條記錄編號的偏移量

  

    形成一種鏈表管理方式:每條記錄的數據部分可以看成一個結點

   

   把他抽象一下就得到了 下圖這種方式

   

 

   但是為什么會有倒着指的情況存在呢?

  (圖 A )

  

 

  假如刪除第二條記錄:

   

 

  最后,被刪除的第二條記錄被移出了上面提到的,存儲有用記錄的鏈表

        

 

   如果把整個物理空間擴大,找到其他同樣也是被刪除的記錄。實際上這些被刪除的記錄,會被標記為空閑狀態(管理信息中有標志位)

   然后采用實際有用的數據相同的鏈接方式,連接成一條鏈表,稱為空閑鏈表

  

 

   下次再插入一條數據的時候,如果從空閑鏈表中找到了符合要插入記錄大小的空閑空間(上圖白色部分)就會把這一部分分配出去

   下圖綠色的部分是新記錄,當然新記錄不一定會占滿之前留下的空閑空間

   藍色的那條指向,是一條倒着的指向,也就可以解釋之前圖 A  上為什么有倒着的鏈表指向了

  

 

   所以,一個物理上的數據中的記錄是邏輯上按照鏈表順序連接起來的,並且是按照主鍵遞增的順序連接成一條單鏈表

   之前說過,4字節的num是主鍵,如果刪除的是 主鍵 = 2 的記錄,那么最后物理上看起來是這樣的:

  

 

   新增加的記錄,主鍵是 7,占用了被刪掉的記錄(主鍵 = 2)的位置(不一定能占滿,上圖是假設占滿了)

   之所以說這條鏈表是邏輯上主鍵遞增的,是因為在物理上這條鏈表並不是主鍵遞增,上圖最明顯的不是遞增特點表現在7插在了1和3之間

   我們把下圖的這一塊稱為一個數據頁,數據頁是 Innodb 磁盤存儲管理的最小單位。當然,實際上數據頁不會像下圖這樣才幾條記錄,下圖只是一個迷你版的表示

  

 

    默認數據頁真正大小一般是16 KB , 真正看起來可能是密密麻麻一大片:

  

 

   每一頁都持有上一頁和下一頁在物理文件中的編號(地址)頁和頁之間可以串起來:

  (實際上是頁結構中的File Header部分保存了上一頁/下一頁在表空間文件中的偏移量(編號)

   如果一個獨立的表空間文件(.idb) 的大小是1GB,每個頁的大小是 16KB, 那么總共有1GB / 16KB = 65536個頁

  下文均討論聚簇索引

 

  

   (下文的B+樹都是簡化的,實際上B樹節點的度不會那么小)

   這些頁都是 Innodb 的 B+ 樹存儲結構中的 數據頁節點,也就是葉子節點

   可以加上非葉子節點(索引節點),讓他成為一顆完整的 B+ 樹:

   

 

   現在大概有一個存儲結構的大體認識了,來解決一個比較深入的問題:上圖的索引節點是什么,怎么通過這些索引節點做查找

   首先了解表的存儲結構:如果使用獨立表空間,表的索引和記錄將會存儲在一個獨立的idb文件中

   idb文件可以按照規定好的數據頁大小切分成若干頁

  

 

 

   每個數據頁都有自己獨特的頁號,其實就是頁的偏移量,可以唯一表示一個數據頁

  

 

 

   需要注意的是物理頁的物理順序和邏輯順序可能不一樣,比如:

  

 

 

   數據頁無需的結果可能是這樣的:

  

   聚簇索引頁的記錄只是簡單的把頁的最小主鍵值和頁的頁號關聯起來

  

 

 

  聚簇索引頁的上一層索引頁(邏輯上)也只是簡單的記錄下層索引頁最小主鍵值和頁號的映射

  

 

 

   當然,Innodb的B+樹的扇出度 (fan out)是很高的,像上圖這樣少量的數據頁一般只有一層索引節點,且只有一個。

   回到一開始我們的目的,假如我要查詢 主鍵 = 25 的記錄

   

 

 

   找到數據頁4,但是要怎么找記錄呢?innodb會把這片數據頁加載入內存,根據這個數據頁的page Directory進行二分查找

   Page Directory 其實只是一堆偏移量而已

  

 

 

  在上面的頁中,如果我要查找主鍵 = 3 的記錄,那么先設置左指針 l = 第一個page directory 項的位置,右指針 r = 最后一個 page directory項的位置

  根據二分查找,求出中間的位置,然后把中間的 page directory 項讀出來,發現記錄的主鍵是4,比要找的 3 大,那就縮小范圍,把右指針 r 設置成剛才

  算出來的中間項的位置,l 和 r 之間已經沒有沒有page director的項 了,所以從 l 指針指向的記錄開始,一條條往后讀,最多讀取其實記錄的n_owned次

  讀不到就表示目標不存在,n_owned其實表示的就是當前記錄到下一個Page Directory有指向的記錄之間有多少條記錄,這些記錄的查詢都是歸當前記錄管

 

 

   

 

   

 

      

 

   

 

 

      

 

   所以根據索引只能查到數據頁,把頁讀進內存在進行二分查找,因為是在內存中操作,相比於索引查找時的磁盤操作,可以忽略


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM