INNODB 頁節點資料的儲存方式、資料鏈、刪除鏈的學習和實驗總結

gaopengtttt發表於2016-03-29
前文:
關於MYSQL INNODB index page header學習和實驗總結 
http://blog.itpub.net/7728585/viewspace-2063921/
關於INNODB SYSTEM RECORD infimum和supremum的學習和實驗研究 
http://blog.itpub.net/7728585/viewspace-2065464/

所用到的工具是自己寫的mysqlblock和bcview,
我放到了百度雲盤

供大家下載和使用
本文只討論COMPACT行模式

資料:
mysql> select * from km1;
+------+---------+
| id   | name    |
+------+---------+
|    2 | gaopeng |
|    4 | gaopeng |
|    5 | gaopeng |
|    6 | gaopeng |
|    7 | gaopeng |
|    8 | gaopeng |
+------+---------+
6 rows in set (0.04 sec)

上一篇文章已經從infimum找到了第一行資料的偏移量
為99+65=164
同時取出了第一行資料:
bcview km1.ibd 16 164 30|more
current block:00000003--Offset:00164--cnt bytes:30--data is:000001cc64260000002d0272d300000d1201108000000267616f70656e67
分解一下資料
000001cc6426   ROWID
0000002d0272   transaction id
d300000d120110 roll pointer
80000002       資料2,這裡8出現在第15位,可能為符號位
67616f70656e67 資料'gaopeng'的ascII值

那麼我們解析來介紹關於CLUSTER KEY-LEAF BLOCK的相關的部分
很顯然我這裡的表只有一個塊,因為資料很少。所以先介紹這個
,因為所謂的infimum的offset是指向的資料的開頭,而行頭資訊
記錄在offset-N的位置,N不確定看了如下就知道了


           variable field lengths  (1-2 bytes* var )
           nullable field bitmap   (1 bit * null field)
           info flags              (4 bits)
           number of records owned (4 bits)
           order                   (13 bits)
           record type             (3 bits)
           next record offset      (2 bytes)
offset ----cluster key fields      (N bytes)
           transaction id          (6 bytes)
           roll pointer            (7 bytes)
           non-key fields          (M bytes)

1、variable field lengths
   每個可變長度的變數型別儲存一個長度如varchar,對於固定長度的比如INT不記錄。
   如果不存在可變長度的變數型別,至少佔用一個位元組為00。
2、nullable field bitmap
   每個NULL值佔用一個一位(bit),如果不滿一個位元組按一個位元組算,如果不存在NULL值
   至少佔用一個位元組為00。
3、info flags
   這4位(4bits)標示是一個行標識,其中binary 0001表示非葉節點最小的行
   其中binary 0010表示是刪除的行,而infimum和supremum行在我測試資料庫中為binary 0000
4、number of records owned
   這4位(4bits)表示在本page directory(槽)中的記錄數,關於槽的概念後面詳細探討
5、order
   這13位(13bits)表示記錄插入到塊中順序,INFIMUM恆等於0而SPREMUM恆等於1,而資料行的ORDER從2開始,這裡的order
   我實驗得出的結論為實際物理空間的順序
6、record type
   這3位(3bits)表示記錄的型別,supermum恆等於3及binary 011,infimum恆等於2及binary010,節點指標為1及001,資料行為000
7、next record offset
   這2個位元組是按照CLUSTER KEY值排序的,也就是說他的順序和order沒有任何聯絡,order是插入的順序 
   在INFIMUM中表示的是第一個行的偏移量這個偏移量是當前記錄的位置+offset,這個offset直接指向了資料而相關的行頭在offset-n開始n為行頭的開銷。
   當然supermum為的偏移量就是NULL空指標了。
8、cluster key fields
   這N個位元組代表主鍵位元組數,沒有就是ROWID佔用6位元組
9、transaction id
   這6個位元組為最後一次修改本行的事物ID
10、roll pointer
    這7個位元組是用於支援MVCC多版本的回退指標,
    1bit 標識
    7bit 回退段ID
    4bytes 回滾段頁號
    2bytes 回滾段頁的偏移量
   這幾位在討論MVCC的時候詳細研究
11、non-key fields 
    也就是M個位元組的非主鍵欄位的資料了。
  
那我們可以完整的取出第一行
我們計算一下我這裡包含一個變數 varchar 1個位元組,沒有NULL值
那行頭的位元組就是1BYTES+1bytes+4BIT+4BIT+13BIT+3BIT+2BYTES=7BYTES
而偏移量為
99+65=164-行頭7bytes=157byes
bcview km1.ibd 16 157 37|more
這裡的37=行頭7BYTES+
         CLUSTER KEY(我是ROWID 6BYTES)+
         transaction id(6BYTES)+
         roll pointer(7BYTES)+
         non-key fields(INT 4BYTES+VARCHAR(7BYTES))
current block:00000003--Offset:00157--cnt bytes:37--data is:0700000018004a000001cc64260000002d0272d300000d1201108000000267616f70656e67
分解一下資料
0X07       varchar 實際資料'gaopeng'的長度
0X00       null型別標示位元組我這裡沒有NULL
0X0        我這裡是0他既不是刪除行也不是非頁節點的最小行,可以理解在也節點中刪除的行在這4位上才有0010的值,隨後測試
0X0        記錄在槽0上
0X0018     轉換為二進位制0000 0000 0001 1000 前13位為0000 0000 0001 1=十進位制的3 為什麼是3呢?不是
           說從2開始嗎?因為我這裡本來的第一行插入的資料被我DELETE掉了,而空間得到從用,OFFSET排序是按照CLUSTER KEY排序的。
           000為record type,很明顯我這裡是資料行,當然也就是000
0X004a     下一個資料的偏移量十進位制74
0X000001cc6426 ROWID
0X0000002d0272d3 事物ID
0X00000d120110   回滾指標
0X80000002       帶符號的int型別的2
0X67616f70656e67 資料'gaopeng'

那我們接下來尋找第二條資料
164+74,由於資料格式一樣,
直接
164+74-行頭7bytes=231
取37位元組

bcview km1.ibd 16 231 37|more

current block:00000003--Offset:00231--cnt bytes:37--data is:0700000028ffdb000001cc64280000002d0278d700000d0601108000000467616f70656e67
分解資料
07     同上
00     同上
0      同上
0      同上
0028   0000 0000 0010 1000   
       0000 0000 0010 1=十進位制的5 
                       000 表示是普通資料
ffdb   這裡注意了負數儲存方式是以補碼的方式,負數說明我們的偏移量回退了,也就是使用了DELETE的空間
       ffdb 也就是-37大家可以自行計算
000001cc6428 同上
0000002d0278d7 同上
00000d060110 同上
80000004     實際資料4
67616f70656e67 實際資料'gaopeng'

那我們接下來尋找第三條資料

164+74+(-37)-7=194

bcview km1.ibd 16 194 37|more
current block:00000003--Offset:00194--cnt bytes:37--data is:0700000020ffb6000001cc65000000002d062bab00000d0c01108000000567616f70656e67

同樣分解資料
07
00
0
0
0020       這裡分解同上 0000 0000 0010 0=十進位制4 這是order 表示這個資料在上條資料插入之前,但是OFFSET是按照ROWID排序的。
ffb6       任然是補碼的方式 實際就是-74
000001cc6500
0000002d062bab 
00000d0c0110
80000005   實際資料5
67616f70656e67 實際資料'gaopeng'

再來第四條資料
164+74+(-37)+(-74)-7=120 這裡來到了物理的第一行資料

bcview km1.ibd 16 120 37|more
current block:00000003--Offset:00120--cnt bytes:37--data is:07000000100094000001cc66000000002d0a2cab00000d0c01108000000667616f70656e67
分解資料
07
00
0
0
0010    0000 0000 0000 1000
        0000 0000 0000 1=十進位制2
0094    偏移量
000001cc6600
0000002d0a2cab
00000d0c0110
80000006   實際資料6
67616f70656e67 實際資料'gaopeng'
接下來的尋找剩下的2條資料我就自己完成了
ID=7 實際位置 164+74+(-37)+(-74)+148-7=268    bcview km1.ibd 16 268 37|more
ID=8 實際位置 164+74+(-37)+(-74)+148+37-7=305 bcview km1.ibd 16 305 37|more
實際上我們大概得出了一個連結串列
infimum order 0  offset 65
-->實際資料 id=2 rowid 000001cc6426 order 3  offset 74
-->實際資料 id=4 rowid 000001cc6428 order 5  offset -37
-->實際資料 id=5 rowid 000001cc6500 order 4  offset -37
-->實際資料 id=6 rowid 000001cc6600 order 2  offset 148
-->實際資料 id=7 rowid 000001cc6700 order 6  offset 37
-->實際資料 id=8 rowid 000001cc6800 order 7  offset -200
-->supermum order 1 offset null
注意最後一條資料的-200,實際為起始位置為164+74+(-37)+(-74)+148+37-200=112
那麼我們看到了他的順序確實為ROWID的排序,而ORDER 實際才是物理順序。
同時注意這裡是按照ROWID進行排序的因為沒有主鍵因為並沒有主鍵,這裡並不是按照ID進行
排序的,這裡只是湊巧而已
如果插入一條
mysql> insert into km1 values(1,'gaopeng12');
Query OK, 1 row affected (0.06 sec)
ID=1的資料
bcview km1.ibd 16 342 40|more分解資料得到

-->實際資料 id=8 rowid 000001cc6800 order 7  offset 37
-->實際資料 id=1 rowid 000001cc6900 order 8  offset -237
-->supermum order 1 offset null

此外還需要做一個實驗就是刪除的行是否在這個連結串列出,同時測試刪除行的info flags

刪除剛才插入的資料
mysql> delete from km1 where id=1;
Query OK, 1 row affected (0.11 sec)

-->實際資料 id=8 rowid 000001cc6800 order 7  offset 37
-->實際資料 id=1 rowid 000001cc6900 order 8  offset -237  刪除
-->supermum order 1 offset null
再次檢視

bcview km1.ibd 16 305 37|more
current block:00000003--Offset:00305--cnt bytes:37--data is:0700000038ff38000001cc68000000002d0e2bab00000d0c01108000000867616f70656e67
分解一下
07
0
0
00
0038
ff38           這裡的offset從37變為了-200 顯然刪除的行從連結串列中刪除了,因為這一行直接指向了supermum
000001cc6800   
0000002d0e2bab
00000d0c0110
80000008
67616f70656e67

我們再次檢視
bcview km1.ibd 16 342 39|more
current block:00000003--Offset:00342--cnt bytes:40--data is:09002000400000000001cc69000000002d10323200000d17022d8000000167616f70656e673132

09
0
0
20 binary 0010 0000可以看到這個位元組的前4位變為了0010 確實binary 0010表示是刪除的行
0040           order 也沒有變為8
0000           這裡指標從先前的-237 變為了0 及空指標
000001cc6900     
0000002d103232 
00000d17022d
80000001          
67616f70656e673132

那麼剛才的連結串列
infimum order 0  offset 65
-->實際資料 id=2 rowid 000001cc6426 order 3  offset 74
-->實際資料 id=4 rowid 000001cc6428 order 5  offset -37
-->實際資料 id=5 rowid 000001cc6500 order 4  offset -37
-->實際資料 id=6 rowid 000001cc6600 order 2  offset 148
-->實際資料 id=7 rowid 000001cc6700 order 6  offset 37
-->實際資料 id=8 rowid 000001cc6800 order 7  offset 37
-->實際資料 id=1 rowid 000001cc6900 order 8  offset -237  刪除
-->supermum order 1 offset null

變為了
infimum order 0  offset 65
-->實際資料 id=2 rowid 000001cc6426 order 3  offset 74
-->實際資料 id=4 rowid 000001cc6428 order 5  offset -37
-->實際資料 id=5 rowid 000001cc6500 order 4  offset -37
-->實際資料 id=6 rowid 000001cc6600 order 2  offset 148
-->實際資料 id=7 rowid 000001cc6700 order 6  offset 37
-->實際資料 id=8 rowid 000001cc6800 order 7  offset -200
-->supermum order 1 offset null

並且刪除的資料
-->實際資料 id=1 rowid 000001cc6900 order 8  offset 0  刪除

如此我們驗證了2個事實
1、刪除的行從offset連結串列中刪除
2、確實binary 0010表示是刪除的行

也許還記得在index page header中包含了兩個資訊
first garbage record offset         2bytes 第一行刪除記錄的偏移量
garbage space                       2bytes 刪除的空間大小單位bytes

如果標示了第一個刪除的行是不是,刪除行也有一個連結串列呢?
我們先來看看這2個位元組當前值
first garbage record offset 
bcview km1.ibd 16 44 2|more  (first garbage record offset)
current block:00000003--Offset:00044--cnt bytes:02--data is:015d
當前這個值為0X15d及349 指向了剛才刪除行的OFFSET,我們猜測試著刪除連結串列的開頭
garbage space
 bcview km1.ibd 16 46 2|more (garbage space) 
current block:00000003--Offset:00046--cnt bytes:02--data is:0027
當前這個值為0X27及39 這個值剛好是刪除記錄的佔用空間
及7+6+7+6+4+9位元組

infimum order 0  offset 65
-->實際資料 id=2 rowid 000001cc6426 order 3  offset 74
-->實際資料 id=4 rowid 000001cc6428 order 5  offset -37
-->實際資料 id=5 rowid 000001cc6500 order 4  offset -37
-->實際資料 id=6 rowid 000001cc6600 order 2  offset 148
-->實際資料 id=7 rowid 000001cc6700 order 6  offset 37
-->實際資料 id=8 rowid 000001cc6800 order 7  offset -200
-->supermum order 1 offset null

並且刪除的資料

first garbage record offset
-->實際資料 id=1 rowid 000001cc6900 order 8  offset 0  刪除

我們來刪除

mysql> delete from km1 where id=2;
Query OK, 1 row affected (0.00 sec)

-->實際資料 id=2 rowid 000001cc6426 order 3  offset 74

刪除後
檢視infimum的偏移量
bcview km1.ibd 16 97 2
current block:00000003--Offset:00097--cnt bytes:02--data is:008b
為0X8b為139
那麼第一條資料的位置為
99+139-7=231

我們檢視一下
bcview km1.ibd 16 231 37|more
current block:00000003--Offset:00231--cnt bytes:37--data is:0700000028ffdb000001cc64280000002d0278d700000d0601108000000467616f70656e67
分解
0700000028
ffdb            偏移量還是-37(補碼方式)
000001cc64280000002d0278d700000d060110
80000004        可以看到這個資料ID=4了
67616f70656e67
那麼也就證明刪除的行從資料連結串列中摘除了。

那麼我們的資料連結串列變為
infimum order 0  offset 139
-->實際資料 id=4 rowid 000001cc6428 order 5  offset -37
-->實際資料 id=5 rowid 000001cc6500 order 4  offset -37
-->實際資料 id=6 rowid 000001cc6600 order 2  offset 148
-->實際資料 id=7 rowid 000001cc6700 order 6  offset 37
-->實際資料 id=8 rowid 000001cc6800 order 7  offset -200
-->supermum order 1 offset null

再次檢視
first garbage record offset 
bcview km1.ibd 16 44 2|more
current block:00000003--Offset:00044--cnt bytes:02--data is:00a4
當前這個值為0XA4及164,剛才為0X15d及349
那麼我們看看 164-7就是行的開頭
bcview km1.ibd 16 157 37|more
current block:00000003--Offset:00157--cnt bytes:37--data is:070020001800b9000001cc64260000002d1038350000014527b68000000267616f70656e67
07
0
0
20     --刪除的行
0018   --order為3
00b9   --下一個刪除行的偏移量
000001cc6426 rowid
0000002d103835
0000014527b6
80000002      資料ID=2
67616f70656e67

最後按照這個偏移量來找到ID=1 rowid 000001cc6900的資料
164+185(0Xb9)-7=342
bcview km1.ibd 16 342 39|more
current block:00000003--Offset:00342--cnt bytes:39--data is:09002000400000000001cc69000000002d10323200000d17022d8000000167616f70656e673132
看看這裡的和剛才的
09002000400000000001cc69000000002d10323200000d17022d8000000167616f70656e673132
沒有任何變化
garbage space
bcview km1.ibd 16 46 2|more
current block:00000003--Offset:00046--cnt bytes:02--data is:004c
當前這個值為0X4C及76 這個值剛好是刪除2條記錄佔用的空間
那麼這個刪除連結串列變成了

first garbage record offset 164
-->實際資料 id=2 rowid 000001cc6426 order 3  offset 185
-->實際資料 id=1 rowid 000001cc6900 order 8  offset 00

隨後我又進行了一次測試,發現在刪除連結串列中first garbage record offset總是指向最近被刪除的一條的記錄的偏移量,而空間從用總是
先使用first garbage record offset指向的空間,使用完成後加入資料連結串列,而first garbage record offset指向下一個節點的位置。
那麼我們可以描述刪除連結串列實際準守一個後入先出的原則,這個有點像棧空間的使用,這個也可以理解,因為在資料連結串列中為了保證
資料的有序讀取必須是按CLUSTER KEY排序的,而在刪除連結串列中沒有這樣需求,簡單實用這種類似棧的連結串列更加簡單。



整個計算過程比較繁瑣,
最後總結一下:
1、一個BLOCK中有2個連結串列一個是資料連結串列,一個是刪除資料的連結串列
  資料連結串列的開頭和結尾是infimum和supermum,
  刪除連結串列的開頭是first garbage record offset,結尾就是最後一個刪除的塊。
  next offset為0000空指標
2、 在資料連結串列中next record offset 是按照CLUSTER KEY 大小進行排序的或者是ROWID
    而在刪除連結串列中first garbage record offset總是指向最近被刪除的一條的記錄的偏移量,next record offset 的順序就是刪除的順序,
    刪除連結串列的空間從用類似棧空間的使用遵循後入先出的原則。
    

3、order 是物理位置的排序,他展示了資料在block中的物理位置,infimum和supermum
  分別為0和1,因為他們總是在開始的。
4、info flags在頁節點中除非刪除了行才標記為 binary 0010
5、刪除的行從資料連結串列中摘除,然後掛載到了刪除連結串列中
6、刪除的行的資料會被重用,在從用之前,delete的資料理論上是可以恢復的,因為他們都在刪除連結串列中
7、關於負的偏移量,也就是刪除後從用的空間,是以補碼的方式給出的
  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2071787/,如需轉載,請註明出處,否則將追究法律責任。

相關文章