Mysql MYISAM存儲引擎 數據存儲結構

MYSQL 的內部數據存儲一直沒有很好的文檔. 自己有空研究了一下.[@more@]

1.用單字段來分析行數據:

1.1 建立測試數據
drop table if exists heyf_5 ;
create table heyf_5 (name varchar(50)) type myisam DEFAULT CHARSET=latin1;
insert into heyf_5 values ('a'),('b'),('c');

[root@alisoft-test1 test]# system hexdump /opt/mysql/data/test/heyf_5.MYD
--------------------------------------------------
0000000 0003 0d03 01fe 0061 0000 0000 0000 0000
0000010 0000 0000 0003 0d03 01fe 0062 0000 0000
0000020 0000 0000 0000 0000 0003 0d03 01fe 0063
0000030 0000 0000 0000 0000 0000 0000
000003c


1.2 開始分析數據

ROW1: 0003 0d03 01fe 0061 0000 0000 0000 0000 0000 0000
ROW2: 0003 0d03 01fe 0062 0000 0000 0000 0000 0000 0000
ROW3: 0003 0d03 01fe 0063 0000 0000 0000 0000 0000 0000
------------------------------

我們拿第一行數據來分析:
由於數據存儲在硬盤裏時,雙字節是低位先存儲,高位後存儲.所以我們讀數據的時候要反過來一下:

ROW1: 03 00 03 0d fe 01 61 00 ...

其中:
---------------------------------------------
03 : start of header - Block type, see mi_dynrec.c, _mi_get_block_info()
00 03 : actual length (varchar存儲的空間爲; 實際字符長度+2)
0d : usused length
fe : flags (0 表示不爲空,1表示爲空)
01 : 該行中該字段實際數據的長度(變長字段纔有)
61 : 實際存儲的數據值: 'a'
00 : 未使用的空間(包括到下一行前的所有00)
---------------------------------------------

1.3 我們驗證上面的假設:

update heyf_5 set name='aaaaaa' where name='a';
update heyf_5 set name= null where name='b';
[root@alisoft-test1 test]# system hexdump /opt/mysql/data/test/heyf_5.MYD
-------------------------------------------------
0000000 0003 0808 06fe 6161 6161 6161 0000 0000
0000010 0000 0000 0003 0e02 00ff 0000 0000 0000
0000020 0000 0000 0000 0000 0003 0d03 01fe 0063
0000030 0000 0000 0000 0000 0000 0000


取出第1行數據:
0003 0808 06fe 6161 6161 6161 0000 0000 0000 0000
轉換一下:
03 00 08 08 fe 06 61 61 61 61 61 61 00 00 00 00 00 00 00 00

其中:
------------------------------------
03 : start of header - Block type
00 08 : 實際字符長度 = 6+2 B
08 : 未使用的空間: 8 B
fe : flags (0 表示不爲null,1表示爲null)
06 : 該行中該字段實際數據的長度6
61 61 : 實際存儲的值: 'aa'
00 : 未使用的空間(剛好8個)


取出第2行數據:
0003 0e02 00ff 0000 0000 0000 0000 0000 0000 0000
轉換一下:
03 00 02 0e ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

其中:
------------------------------------
03 : start of header - Block type
00 02 : 實際字符長度 = 0+2 B
0e : 未使用的空間: 14 B
ff : --> 1111 1111 因爲只有一個字段,標誌用最後1位爲表示,是1表示爲NULL
00 : 該行中該字段實際數據的長度 0
00 ... : 未使用的空間(剛好14個)


1.4 行鏈接的情況分析

從上面看, 在行初始化時,系統給VARCHAR字段預留了14個字節的空間.
如果我們的字段值在14個字符範圍內,就不會溢出. 下面我們來看一下溢出(就是需要行鏈接)的情況:

update heyf_5 set name='bbbbbaaaaabbbbbaaaaa' where name is null;
update heyf_5 set name='cccccdddddcccccdddddzzzzzee' where name='c';
system hexdump /opt/mysql/data/test/heyf_5.MYD
---------------------------------------------
0000000 0003 0808 06fe 6161 6161 6161 0000 0000
0000010 0000 0000 0005 0016 0007 0000 0000 0000
0000020 fe3c 6214 6262 6262 0005 001b 0007 0000
0000030 0000 0000 fe50 6319 6363 6363 0009 010f
0000040 6161 6161 6261 6262 6262 6161 6161 0061
0000050 0009 0216 6464 6464 6364 6363 6363 6464
0000060 6464 7a64 7a7a 7a7a 6565 0000

我們上面更新的是第二行和第三行的數據:
ROW2: 0005 0016 0007 0000 0000 0000 fe3c 6214 6262 6262
ROW3: 0005 001d 0007 0000 0000 0000 fe50 631b 6363 6363

同時我們看到,在地址3C後面有其它數據:
0000030 0009 010f
0000040 6161 6161 6261 6262 6262 6161 6161 0061
0000050 0009 010f 6464 6464 6364 6363 6363 6464
0000060 6464 0064
這個我們回頭來分析:


我們先把行的數據轉一下:
ROW2:05 00 16 00 07 00 00 00 00 00 00 00 3c fe 14 62 62 62 62 62
ROW3:05 00 1d 00 07 00 00 00 00 00 00 00 50 fe 1b 63 63 63 63 63

ROW2數據,其中:
--------------------------------------------------------
05 : 只要發生行鏈接type就會變成05
00 16 : 實際字符長度 = 20+2= 22
00 : 未使用的空間: 0 B
07 00 00 00 00 00 00 00 3c :
在這間多出了這麼5個字節的地址,這個地址其實就是該行數據的鏈接地址
ROW2爲3C, ROW3爲50

fe : flags (0 表示不爲null,1表示爲null)
14 : 該行中該字段實際數據的長度:20
5個62/63 : 實際存儲的值: 'bbbbb'/'ccccc'
---------------------------------------------------------

在這裏我們看到ROW2的數據只有'bbbbb',那麼其他數據在哪裏呢?
MYSQL會根據類型知道需要鏈接,而鏈接地址正是:07 00 00 00 00 00 00 00 3c

我們從3C開始讀鏈接塊數據:
0009 010f 6161 6161 6261 6262 6262 6161 6161 0061
轉換一下:
09 00 0f 01 61 61 61 61 61 62 62 62 62 62 61 61 61 61 61 00

其中:
--------------------------------------------------------
09 : 還是類型: 代表這是鏈接數據
00 0f : 該段實際字符長度 = 15
01 : 空閒的空間字符數
61 61 61 61 61 62 62 62 62 62 61 61 61 61 61 : 具體的值
00 : 空閒的空間
---------------------------------------------------------


同理我們來看看ROW3的鏈接數據: (從50開始)
0009 0216 6464 6464 6364 6363 6363 6464 6464 7a64 7a7a 7a7a 6565 0000
轉換一下:
09 00 16 02 'dddddcccccdddddzzzzzee' 00 00

其中:
--------------------------------------------------------
09 : 還是類型: 代表這是鏈接數據
00 16 : 該段實際字符長度 = 22
02 : 未使用的空間: 2 B
... : 是具體的值和00
---------------------------------------------------------

2.用多個字段來分析行數據:

drop table if exists heyf_5 ;
create table heyf_5 (id int ,name varchar(50),id1 int ) type myisam ;
insert into heyf_5 values (100,'aaa',3),(2,'bb',12),(3,'c',4);
system hexdump /opt/mysql/data/test/heyf_5.MYD
==================================================
0000000 0003 020e f800 0064 0000 6103 6161 0003
0000010 0000 0000 0003 030d f800 0002 0000 6202
0000020 0c62 0000 0000 0000 0003 040c f800 0003
0000030 0000 6301 0004 0000 0000 0000
==================================================

ROW1: 0003 020e f800 0001 0000 6103 6161 0003 0000 0000
轉換成:
03 00 0e 02 00 f8 01 00 00 00 03 61 61 61 03 00 00 00 00 00

其中:
-----------------------------------------
03 : type
00 0e : 13 = 4(int)+[ 3+2 (varchar)] + 4(int)
02 : 空閒空間2個
00 f8 : 1111 1000 --> 表示三個字段,並不爲NULL
下面爲具體的數據:
01 00 00 00 : INT 4B 值爲1
03 61 61 61 : 因爲是變長,03表示這個值的長度,後面跟具體的值
03 00 00 00 : INT 4B 值爲3
-----------------------------------------

3.小結:

3.1 如果是一個新行:

每行至少分配14個字節+頭6個=20個B,如果超過14個B,按實際需要分配.
行數據的格式爲:
---------------------------------------------
03 (LEN:1B): start of header - Block type, see mi_dynrec.c, _mi_get_block_info()
00 03 (LEN:2B): actual length (varchar存儲的空間爲; 實際字符長度+2)
0d (LEN:1B): 分配給該行的空間中,未使用的字節數
00 fe (LEN:XB): flags (0 表示不爲空,1表示爲空) (這裏是一個字節還是兩個字節,根據.FRM定義取得)

--數據部分:
--第一個字段:
01 (LEN:XB): 如果是變長類型,在每個字段數據開始前會有1-N個字段來表明該字段數據的實際長度,
如果是固定長度類型的數據,則沒有這個標識.
DATA (LEN:XB): 實際存儲的值: 'a',長度根據第二個標識定.
--第二個字段:
...
--第三個字段:
...
00 (LEN:XB): 未使用的空間(長度根據第三個標識定)
---------------------------------------------


3.2 如果是一個被更新過並且產生了鏈接的行:

3.2.1 行數據的格式爲:
--------------------------------------------------------
05 (LEN:1B): 只要發生行鏈接type就會變成05
00 16 (LEN:1B): 實際字符長度 = 20+2= 22
00 (LEN:1B): 未使用的空間: 0 B
07 00 00 00 00 00 00 00 3c (LEN:9B): 行數據的鏈接地址
fe (LEN:XB): flags (0 表示不爲null,1表示爲null)

-- 數據部分:
--第一個字段:
14 (LEN:XB): 如果是變長類型,在每個字段數據開始前會有1-N個字段來表明該字段數據的實際長度,
如果是固定長度類型的數據,則沒有這個標識.
DATA (LEN:XB): 實際存儲的值: 'a',長度根據第二個標識定.
--第二個字段:
...
---------------------------------------------------------

3.2.2 並且,鏈接部分的數據格式爲:
--------------------------------------------------------
09 : 類型: 代表這是鏈接數據
00 16 : 該段實際字符長度
02 : 分配給鏈接行的空間中,未使用的空間: 2 B
-- 數據部分:
第一個字段:
14 (LEN:XB): 如果是變長類型,在每個字段數據開始前會有1-N個字段來表明該字段數據的實際長度,
如果是固定長度類型的數據,則沒有這個標識.
DATA (LEN:XB): 實際存儲的值: 'a',長度根據第二個標識定.
第二個字段:
...
00 : 未使用的空間
XXX : 下一行
---------------------------------------------------------

3.3 被刪除的行:
如果該行被刪除了,那麼該行的TYPE=00,然後數據部分都會被置爲FF;

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