首页 > 数据库 > mysql教程 > MySQL InnoDB的存储结构总结

MySQL InnoDB的存储结构总结

WBOY
发布: 2016-06-07 15:46:46
原创
935 人浏览过

背景: 再一次看完MySQL 技术内幕-Innodb存储引擎 一书的的第4章。对前面五节的内容做又有了新的认识,顺便做下笔记。先了解下相关的概念: 表空间: INNODB 所有数据都存在表空间当中(共享表空间),要是开启innodb_file_per_table,则每张表的数据会存到单

背景:
 再一次看完 一书的的第4章。对前面五节的内容做又有了新的认识,顺便做下笔记。先了解下相关的概念:
表空间:INNODB 所有数据都存在表空间当中(共享表空间),要是开启innodb_file_per_table,则每张表的数据会存到单独的一个表空间内(独享表空间)。
独享表空间包括:数据,索引,插入缓存,数据字典。共享表空间包括:Undo信息(不会回收),双写缓存信息,事务信息等。
段(segment):组成表空间,有区组成。
区(extent):有64个连续的页组成。每个页16K,总共1M。对于大的数据段,每次最后可申请4个区。
页(page):是INNODB 磁盘管理的单位,有行组成。
行(row):包括事务ID,回滚指针,列信息等。

目的1:
了解表空间各个页的信息和溢出行数据存储的信息。通过该书作者蒋承尧编写的工具:http://code.google.com/p/david-mysql-tools/source/browse/trunk/py_innodb_page_type/
3个脚本:
py_innodb_page_info.py

MySQL InnoDB的存储结构总结

MySQL InnoDB的存储结构总结

<span>#</span><span>! /usr/bin/env python </span><span>
#</span><span>encoding=utf-8</span>
<span>import</span><span> mylib
</span><span>from</span> sys <span>import</span><span> argv
</span><span>from</span> mylib <span>import</span><span> myargv

</span><span>if</span> <span>__name__</span> == <span>'</span><span>__main__</span><span>'</span><span>:
    myargv </span>=<span> myargv(argv)
    </span><span>if</span> myargv.parse_cmdline() ==<span> 0:
        </span><span>pass</span>
    <span>else</span><span>:
        mylib.get_innodb_page_type(myargv)</span>
登录后复制

MySQL InnoDB的存储结构总结

mylib.py

MySQL InnoDB的存储结构总结View Code

include.py

MySQL InnoDB的存储结构总结View Code

测试1:

root<span>@localhost</span> : test <span>02</span>:<span>26</span>:<span>13</span><span>></span><span>create</span> <span>table</span> tt(id <span>int</span> auto_increment,name <span>varchar</span>(<span>10</span>),age <span>int</span>,address <span>varchar</span>(<span>20</span>),<span>primary</span> <span>key</span> (id))engine<span>=</span><span>innodb;
Query OK, </span><span>0</span> rows affected (<span>0.17</span><span> sec)
root</span><span>@zhoujy</span>:<span>/</span><span>var</span><span>/</span>lib<span>/</span>mysql<span>/</span>test# ls <span>-</span><span>lh tt.ibd 
</span><span>-</span>rw<span>-</span>rw<span>--</span><span>-- 1 mysql mysql 96K 2012-10-17 14:26 tt.ibd</span>
登录后复制

查看ibd:

MySQL InnoDB的存储结构总结

root@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> python py_innodb_page_info.py /var/lib/mysql/test/tt.ibd -v</span>
page offset 00000000, page type <file space header><span>
page offset </span>00000001, page type <insert buffer bitmap><span>
page offset </span>00000002, page type <file segment inode><span>
page offset </span>00000003, page type <b-tree node>, page level  ---叶子节点<span>
page offset </span>00000000, page type <freshly allocated page><span>
page offset </span>00000000, page type <freshly allocated page><span>
Total number of page: </span>6<span>: 
Freshly Allocated Page: </span>2<span>
Insert Buffer Bitmap: </span>1<span>
File Space Header: </span>1<span>
B</span>-tree Node: 1<span>
File Segment inode: </span>1</freshly></freshly></b-tree></file></insert></file>
登录后复制

MySQL InnoDB的存储结构总结

解释:
Total number of page: 总页数
Freshly Allocated Page:可用页
Insert Buffer Bitmap:插入缓存位图页
Insert Buffer Free List:插入缓存空闲列表页
B-tree Node:数据页

Uncompressed BLOB Page:二进制大对象页,存放溢出行的页,即溢出页
上面得到的信息是表初始化大小为96K,他是有 Total number of page * 16 得来的。1个数据页,2个可用页面。

root<span>@localhost</span> : test <span>02</span>:<span>42</span>:<span>58</span><span>></span><span>insert</span> <span>into</span> tt <span>values</span>(name,age,address) <span>values</span>(<span>'</span><span>aaa</span><span>'</span>,<span>23</span>,<span>'</span><span>HZZZ</span><span>'</span>);
登录后复制

疑惑:为什么没有申请区?区是64个连续的页,大小1M。那么表大小也应该是至少1M。但是现在只有96K(默认)。原因是因为每个段开始的时候,先有32个页大小的碎片页存放数据,使用
完之后才是64页的连续申请,最多每次可以申请4个区,保证数据的顺序。这里看出表大小增加是按照至少64页的大小的空间来增加的,即1M增加。
验证:
填充数据,写满这32个碎片页,32*16 = 512K。看看是否能申请大于1M的空间。

MySQL InnoDB的存储结构总结View Code

"额外"页:4个
page offset 00000000, page type :文件头空间页
page offset 00000001, page type :插入缓存位图页
page offset 00000002, page type :文件段节点
page offset 00000003, page type , page level :根页
碎片页:32个
page type , page level
总共36个页,ibd大小 576K的由来:32*16=512K(碎片页)+ 4*16=64(额外页),这里开始要是再插入的话,应该申请最少1M的页:

MySQL InnoDB的存储结构总结

root@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> ls -lh /var/lib/mysql/test/tt.ibd </span>
-rw-rw---- 1 mysql mysql 2.0M 2012-10-17 16:10 /var/lib/mysql/test/<span>tt.ibd
root</span>@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> python py_innodb_page_info.py /var/lib/mysql/test/tt.ibd</span>
Total number of page: 128<span>:
Freshly Allocated Page: </span>91<span>
Insert Buffer Bitmap: </span>1<span>
File Space Header: </span>1<span>
B</span>-tree Node: 34<span>
File Segment inode: </span>1
登录后复制

MySQL InnoDB的存储结构总结

页从36跳到了128,因为已经用完了32个碎片页,新的页会采用区的方式进行空间申请。信息中看到有很多可用页,正好说明这点。

 ▲溢出行数据存放:INNODB存储引擎是索引组织的,即每页中至少有两行记录,因此如果页中只能存放一行记录,INNODB会自动将行数据放到溢出页中。当发生溢出行的时候,实际数据保存在BLOB页中,数据页只保存数据的前768字节(老的文件格式),新的文件格式(Barracuda)采用完全行溢出的方式,数据页只保存20个字节的指针,BLOB也保存所有数据。如何查看表中有溢出行数据呢?

root<span>@localhost</span> : test <span>04</span>:<span>52</span>:<span>34</span><span>></span><span>create</span> <span>table</span> t1 (id <span>int</span>,name <span>varchar</span>(<span>10</span>),memo <span>varchar</span>(<span>8000</span>))engine <span>=</span>innodb <span>default</span><span> charset utf8;
Query OK, </span><span>0</span> rows affected (<span>0.16</span><span> sec)

root</span><span>@localhost</span> : test <span>04</span>:<span>53</span>:<span>10</span><span>></span><span>insert</span> <span>into</span> t1 <span>values</span>(<span>1</span>,<span>'</span><span>zjy</span><span>'</span>,repeat(<span>'</span><span>我</span><span>'</span>,<span>8000</span><span>));
Query OK, </span><span>1</span> row affected (<span>0.00</span> sec)
登录后复制

 查看ibd:

MySQL InnoDB的存储结构总结

root@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> python py_innodb_page_info.py /var/lib/mysql/test/t1.ibd -v</span>
page offset 00000000, page type <file space header><span>
page offset </span>00000001, page type <insert buffer bitmap><span>
page offset </span>00000002, page type <file segment inode><span>
page offset </span>00000003, page type <b-tree node>, page level <span>
page offset </span>00000004, page type <uncompressed blob page><span>
page offset </span>00000005, page type <uncompressed blob page><span>
Total number of page: </span>6<span>:
Insert Buffer Bitmap: </span>1<span>
Uncompressed BLOB Page: </span>2<span>
File Space Header: </span>1<span>
B</span>-tree Node: 1<span>
File Segment inode: </span>1</uncompressed></uncompressed></b-tree></file></insert></file>
登录后复制

MySQL InnoDB的存储结构总结

从信息中看到,刚才插入的一行记录,已经溢出了,保存到了2个BLOB页中()。因为1页只有16K,又要存2行数据,所以每行记录最好小于8K,而上面的远远大于8K,所以被溢出了。当然这个也不是包括特大字段,要是一张表里面有5个字段都是varchar(512)【多个varchar的总和大于8K就可以】,也会溢出:

root<span>@localhost</span> : test <span>05</span>:<span>08</span>:<span>39</span><span>></span><span>create</span> <span>table</span> t2 (id <span>int</span>,name <span>varchar</span>(<span>1000</span>),address <span>varchar</span>(<span>512</span>),company <span>varchar</span>(<span>200</span>),xx <span>varchar</span>(<span>512</span>),memo <span>varchar</span>(<span>512</span>),dem <span>varchar</span>(<span>1000</span>))engine <span>=</span>innodb <span>default</span><span> charset utf8;
Query OK, </span><span>0</span> rows affected (<span>0.17</span><span> sec)
root</span><span>@localhost</span> : test <span>05</span>:<span>08</span>:<span>43</span><span>></span><span>insert</span> <span>into</span> t2 <span>values</span>(<span>1</span>,repeat(<span>'</span><span>周</span><span>'</span>,<span>1000</span>),repeat(<span>'</span><span>我</span><span>'</span>,<span>500</span>),repeat(<span>'</span><span>丁</span><span>'</span>,<span>500</span>),repeat(<span>'</span><span>啊</span><span>'</span>,<span>500</span>),repeat(<span>'</span><span>噢</span><span>'</span>,<span>500</span>),repeat(<span>'</span><span>阿a</span><span>'</span>,<span>500</span>));
登录后复制

1000+500+500+500+500+500=3500*3>8000字节;行会被溢出:

MySQL InnoDB的存储结构总结

root@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> python py_innodb_page_info.py /var/lib/mysql/test/t2.ibd -v</span>
page offset 00000000, page type <file space header><span>
page offset </span>00000001, page type <insert buffer bitmap><span>
page offset </span>00000002, page type <file segment inode><span>
page offset </span>00000003, page type <b-tree node>, page level <span>
page offset </span>00000004, page type <uncompressed blob page><span>
page offset </span>00000000, page type <freshly allocated page><span>
Total number of page: </span>6<span>:
Insert Buffer Bitmap: </span>1<span>
Freshly Allocated Page: </span>1<span>
File Segment inode: </span>1<span>
B</span>-tree Node: 1<span>
File Space Header: </span>1<span>
Uncompressed BLOB Page: </span>1</freshly></uncompressed></b-tree></file></insert></file>
登录后复制

MySQL InnoDB的存储结构总结

页存放真正的数据,那数据页到底存放什么?用hexdump查看:

root@zhoujy:/home/zhoujy/jiaoben/read_ibd<span>#</span><span> hexdump -C -v  /var/lib/mysql/test/t1.ibd  > t1.txt</span>
登录后复制

查看ibd:

MySQL InnoDB的存储结构总结View Code

文本中刚好是48行,每行16字节。48*16=768字节,刚好验证了之前说的:数据页只保存数据的前768字节(老的文件格式)。

总结1:
     通过上面的信息,可以能清楚的知道ibd表空间各个页的分布和利用信息以及表空间大小增加的步长;特别注意的是溢出行,一个页中至少包含2行数据,如果页中存放的行数越多,性能就越好。

************************************
************************************
目的2:
     
了解表空间如何存储数据,以及对NULL值的存储。

测试2:
在测试前先了解INNODB的存储格式(row_format)。老格式(Antelope):Compact,Redumdant;新格式(Barracuda):Compressed ,Dynamic。

这里测试指针对默认的存储格式。
Compact行记录方式如下:

<span><strong>   |变长字段长度列表(1~2字节)|NULL标志位(1字节)|记录头信息(5字节)|RowID(6字节)|事务ID(6字节)|回滚指针(7字节)|</strong></span>
登录后复制

上面信息除了 "NULL标志位"[表中所有字段都定义为NOT NULL],"RowID"[表中有主键] ,"变长字段长度列表" [没有变长字段] 可能不存在外,其他信息都会出现。所以一行数据除了列数据所占用的字段外,还需要额外18字节。

一:字段全NULL

MySQL InnoDB的存储结构总结

mysql<span>></span> <span>create</span> <span>table</span> mytest(t1 <span>varchar</span>(<span>10</span>),t2 <span>varchar</span>(<span>10</span>),t3 <span>varchar</span>(<span>10</span>) ,t4 <span>varchar</span>(<span>10</span>))engine<span>=</span>innodb charset <span>=</span> latin1 row_format<span>=</span><span>compact;
Query OK, </span><span>0</span> rows affected (<span>0.08</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>ccc</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.02</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>fff</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.01</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>NULL</span>,<span>NULL</span>,<span>'</span><span>fff</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span> sec)
登录后复制

MySQL InnoDB的存储结构总结

测试数据准备完之后,执行shell命令:

root@zhoujy:/usr/local/mysql/test<span>#</span><span> hexdump -C -v mytest.ibd > /home/zhoujy/mytest.txt</span>
登录后复制

打开mytest.txt文件找到supremum这一行:

MySQL InnoDB的存储结构总结

0000c070  73 75 70 72 65 6d 75 6d  <span>03 02 02 01</span> <span>00</span> <span>00 00 10</span>  |supremum........|   -----------><strong>一行,16字节</strong><span>
0000c080  </span><span>00 25</span> <span>00 00 00 03 b9 00</span>  <span>00 00 00 02 49 01</span> <span>82 00</span>  |.%..........I...|<span>
0000c090  </span><span>00 01 4a 01 10</span> <span>61 62 62  62 62 63 63 63</span> 03 02 02  |..J..abbbbccc...|<span>
0000c0a0  </span>01 00 00 00 18 00 23 00  00 00 03 b9 01 00 00 00  |......<span>#</span><span>.........|</span>
0000c0b0  02 49 02 83 00 00 01 4b  01 10 61 65 65 65 65 66  |.I.....K..aeeeef|<span>
0000c0c0  </span>66 66 <span>03 01</span> <span>06</span> <span>00 00 20  ff a6</span> <span>00 00 00 03 b9 02</span>  |ff..... ........|<span>
0000c0d0  </span><span>00 00 00 02 49 03</span> <span>84 00  00 01 4c 01 10</span> <span>61 66 66</span>  |....I.....L..aff|<span>
0000c0e0  </span><span>66</span> 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |f...............|
登录后复制

MySQL InnoDB的存储结构总结

释:
第一行数据:
03 02 02 01 /*变长字段*/ ---- 表中4个字段类型为varchar,并且没有NULL数据,而且每个字段君小于255。
00 /*NULL标志位,第一行没有null的数据*/
00 00 10 00 25 /*记录头信息,固定5个字节*/
00 00 00 03 b9 00 /*RowID,固定6个字节,表没有主键*/
00 00 00 02 49 01 /*事务ID,固定6个字节*/
82 00 00 01 4a 01 10 /*回滚指针,固定7个字节*/
61 62 62 62 62 63 63 63 /*列的数据*/
第二行数据和第一行数据一样(颜色匹配)。
第三行数据(有NULL值)和第一行的解释的颜色对应起来比较差别:

<span><span>03 02 02 01</span>  VS  </span><span><span>03 01</span><span><span>   ----------</span><span><span>当值为NULL时,变长字段列表<span><span>不会占用</span></span>存储空间。</span></span></span></span>
登录后复制
<span><span>61 62 62  62 62 63 63 63</span> VS </span><span><span>61 66 66 </span><span><span>66</span><span><span>  ---------</span> <span>NULL值没有存储,不占空间</span></span></span></span>
登录后复制

结论:当值为NULL时,变长字段列表不会占用存储空间。NULL值没有存储,不占空间,但是需要一个标志位(一行一个)。

二:字段全NOT NULL

MySQL InnoDB的存储结构总结

mysql<span>></span> <span>create</span> <span>table</span> mytest(t1 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span>,t2 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span>,t3 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span>,t4 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span>)engine<span>=</span>innodb charset <span>=</span> latin1 row_format<span>=</span><span>compact;
Query OK, </span><span>0</span> rows affected (<span>0.03</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>ccc</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.01</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>fff</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.01</span><span> sec)

mysql</span><span>></span> <span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>NULL</span>,<span>NULL</span>,<span>'</span><span>fff</span><span>'</span><span>);
ERROR </span><span>1048</span> (<span>23000</span>): <span>Column</span> <span>'</span><span>t2</span><span>'</span> cannot be <span>null</span>
登录后复制

MySQL InnoDB的存储结构总结

 步骤和上面一样,得到的ibd的结果是:

<span>0000c070  73 75 70 72 65 6d 75 6d  <span>03 02 02 01</span> <span>00 00 10 00</span>  |supremum........|
0000c080  <span>24</span> <span>00 00 00 03 b9 03</span> <span>00  00 00 02 49 07</span> <span>87 00 00</span>  |$..........I....|
0000c090  <span>01 4f 01 10</span> <span>61 62 62 62  62 63 63 63</span> 03 02 02 01  |.O..abbbbccc....|
0000c0a0  00 00 18 ff cb 00 00 00  03 b9 04 00 00 00 02 49  |...............I|
0000c0b0  08 88 00 00 01 50 01 10  61 65 65 65 65 66 66 66  |.....P..aeeeefff|</span>
登录后复制

和上面比较,发现少了NULL的标志位信息。
结论:  NULL值会有额外的空间来存储,即每行1字节的大小。对于相同数据的表,字段中有NULL值的表比NOT NULL的大。

三:1个NULL,和1个''的数据:

MySQL InnoDB的存储结构总结

mysql<span>></span> <span>create</span> <span>table</span> mytest(t1 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span>,t2 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> <span>''</span>,t3 <span>varchar</span>(<span>10</span>) <span>NOT</span> <span>NULL</span> ,t4 <span>varchar</span>(<span>10</span>))engine<span>=</span>innodb charset <span>=</span> latin1 row_format<span>=</span><span>compact;
Query OK, </span><span>0</span> rows affected (<span>0.02</span><span> sec)
mysql</span><span>></span> <span>insert</span> <span>into</span> mytest(t1,t2) <span>values</span>(<span>'</span><span>A</span><span>'</span>,<span>'</span><span>BB</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected, <span>1</span> warning (<span>0.01</span> sec)
登录后复制

MySQL InnoDB的存储结构总结

 步骤和上面一样,得到的ibd的结果是:

0000c070  73 75 70 72 65 6d 75 6d  <span>00 02 01</span> <span>01</span> <span>00 00 10 ff</span>  |supremum........|<span>
0000c080  <span>ef </span></span><span>00 00 00 43 b9 03</span> <span>00  00 00 02 4a 15</span> <span>90 00 00</span>  |....C......J....|<span>
0000c090  </span><span>01 c2 01 10</span> <span>41 42 42</span> 00  00 00 00 00 00 00 00 00  |....ABB.........|
登录后复制

和上面2个区别主要在于变长列表和列数据这里。

结论:列数据信息里表明了 NULL数据和''数据都不占用任何空间,对于变长字段列表的信息,和一对比得出:‘’数据虽然不需要占用任何存储空间,但是在变长字段列表里面还是需要占用一个字节,NULL值不需要占用”,只是NULL会有额外的一个标志位,所以能有个优化的说法:“数据库表中能设置NOT NULL的就尽量设置为NOT NULL,除非确实需要NULL值得。” 在此得到了证明。

上面的测试都是针对VARCHAR的变长类型,那对于CHAR呢?

CHAR 测试:

MySQL InnoDB的存储结构总结

root<span>@localhost</span> : test <span>10</span>:<span>33</span>:<span>35</span><span>></span><span>create</span> <span>table</span> mytest(t1 <span>char</span>(<span>10</span>),t2 <span>char</span>(<span>10</span>),t3 <span>char</span>(<span>10</span>) ,t4 <span>char</span>(<span>10</span>))engine<span>=</span>innodb charset <span>=</span> latin1 row_format<span>=</span>compact;Query OK, <span>0</span> rows affected (<span>0.16</span><span> sec)

root</span><span>@localhost</span> : test <span>10</span>:<span>33</span>:<span>59</span><span>></span><span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>ccc</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span><span> sec)

root</span><span>@localhost</span> : test <span>10</span>:<span>34</span>:<span>09</span><span>></span><span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>ee</span><span>'</span>,<span>'</span><span>fff</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span><span> sec)

root</span><span>@localhost</span> : test <span>10</span>:<span>34</span>:<span>19</span><span>></span><span>insert</span> <span>into</span> mytest <span>values</span>(<span>'</span><span>a</span><span>'</span>,<span>NULL</span>,<span>NULL</span>,<span>'</span><span>fff</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span> sec)
登录后复制

MySQL InnoDB的存储结构总结

打开ibd生成的文件:

MySQL InnoDB的存储结构总结

0000c060  02 00 1b 69 6e 66 69 6d  75 6d 00 04 00 0b 00 00  |...infimum......|<span>
0000c070  </span>73 75 70 72 65 6d 75 6d  <span>00</span> <span>00 00 10 00 41</span> <span>00 00</span>  |supremum.....A..|<span>
0000c080  </span><span>00 0a f5 00</span> <span>00 00 00 81  2d 07</span> <span>80 00 00 00 32 01</span>  |........-.....2.|<span>
0000c090  </span><span>10</span> <span>61 20 20 20 20 20 20  20 20 20</span> <span>62 62 20 20 20</span>  |.a         bb   |<span>
0000c0a0  </span><span>20 20 20 20 20</span> <span>62 62 20  20 20 20 20 20 20 20</span> <span>63</span>  |     bb        c|<span>
0000c0b0  </span><span>63 63 20 20 20 20 20 20  20</span> 00 00 00 18 00 41 00  |cc       .....A.|<span>
0000c0c0  </span>00 00 0a f5 01 00 00 00  81 2d 08 80 00 00 00 32  |.........-.....2|<span>
0000c0d0  </span>01 10 61 20 20 20 20 20  20 20 20 20 65 65 20 20  |..a         ee  |<span>
0000c0e0  </span>20 20 20 20 20 20 65 65  20 20 20 20 20 20 20 20  |      ee        |<span>
0000c0f0  </span>66 66 66 20 20 20 20 20  20 20 <span>06</span> <span>00 00 20 ff 70</span>  |fff       ... .p|<span>
0000c100  </span><span>00 00 00 0a f5 02</span> <span>00 00  00 81 2d 09</span> <span>80 00 00 00</span>  |..........-.....|<span>
0000c110  </span><span>32 01 10</span> <span>61 20 20 20 20  20 20 20 20 20</span> <span>66 66 66</span>  |2..a         fff|<span>
0000c120  </span><span>20 20 20 20 20 20 20 00</span>  00 00 00 00 00 00 00 00  |       .........|
登录后复制

MySQL InnoDB的存储结构总结

和一的varchar比较发现:少了变长字段列表,但是对于char来讲,需要固定长度来存储的,存不到固定长度,也会被填充满。如:20;并且NULL值也不需要占用存储空间。

混合(varchar,char):

MySQL InnoDB的存储结构总结

root<span>@localhost</span> : test <span>11</span>:<span>21</span>:<span>48</span><span>></span><span>create</span> <span>table</span> mytest(t1 <span>int</span>,t2 <span>char</span>(<span>10</span>),t3 <span>varchar</span>(<span>10</span>) ,t4 <span>char</span>(<span>10</span>))engine<span>=</span>innodb charset <span>=</span> latin1 row_format<span>=</span><span>compact;
Query OK, </span><span>0</span> rows affected (<span>0.17</span><span> sec)

root</span><span>@localhost</span> : test <span>11</span>:<span>21</span>:<span>50</span><span>></span><span>insert</span> <span>into</span> mytest <span>values</span>(<span>1</span>,<span>'</span><span>a</span><span>'</span>,<span>'</span><span>b</span><span>'</span>,<span>'</span><span>c</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span><span> sec)

root</span><span>@localhost</span> : test <span>11</span>:<span>22</span>:<span>06</span><span>></span><span>insert</span> <span>into</span> mytest <span>values</span>(<span>11</span>,<span>'</span><span>aa</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>cc</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span> sec)
登录后复制

MySQL InnoDB的存储结构总结

 从上面的表结构中看出:
1,变长字段列表长度:1
2,NULL标志位:1
3,记录头信息:5
4,RowID:6
5,事务ID:6
6,回滚指针:7

idb的信息

MySQL InnoDB的存储结构总结

0000c070  73 75 70 72 65 6d 75 6d  <span>01</span> <span>00</span> <span>00 00 10 00 33</span> <span>00</span>  |supremum......3.| <span>
0000c080  </span><span>00 00 0a f5 07</span> <span>00 00 00  81 2d 1a</span> <span>80 00 00 00 32</span>  |.........-.....2|<span>
0000c090  </span><span>01 10</span> <span>80 00 00 01</span> <span>61 20  20 20 20 20 20 20 20 20</span>  |......a         |<span>
0000c0a0  </span><span>62</span> <span>63 20 20 20 20 20 20  20 20 20</span> 02 00 00 00 18  |bc         .....|<span>
0000c0b0  ff be </span>00 00 00 0a f5 08  00 00 00 81 2d 1b 80 00  |............-...|<span>
0000c0c0  </span>00 00 32 01 10 <span>80 00 00  0b</span> 61 61 20 20 20 20 20  |..2......aa     |<span>
0000c0d0  </span>20 20 20 62 62 63 63 20  20 20 20 20 20 20 20 00  |   bbcc        .|
登录后复制

MySQL InnoDB的存储结构总结

从上信息得出和之前预料的一样:因为表中只有一个varchar字段,所以,变长列表长度就只有:01 
特别注意的是:各个列数据存储的信息:t1字段为int 类型,占用4个字节的大小。第一行:80 00 00 01 就是表示 1 数字;第二行:80 00 00   0b 表示了11的数字。[select hex(11)  == B ],其他的和上面的例子一样。

上面都是latin1单字节字符集的说明,那对于多字节字符集的情况怎么样?

MySQL InnoDB的存储结构总结

root<span>@localhost</span> : test <span>11</span>:<span>52</span>:<span>10</span><span>></span><span>create</span> <span>table</span> mytest(id <span>int</span> auto_increment,t2 <span>varchar</span>(<span>10</span>),t3 <span>varchar</span>(<span>10</span>) ,t4 <span>char</span>(<span>10</span>),<span>primary</span> <span>key</span>(id))engine<span>=</span>innodb charset <span>=</span> utf8 row_format<span>=</span><span>compact;
Query OK, </span><span>0</span> rows affected (<span>0.17</span><span> sec)

root</span><span>@localhost</span> : test <span>11</span>:<span>52</span>:<span>11</span><span>></span><span>insert</span> <span>into</span> mytest(t2,t3,t4) <span>values</span>(<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>bb</span><span>'</span>,<span>'</span><span>ccc</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span><span> sec)

root</span><span>@localhost</span> : test <span>11</span>:<span>55</span>:<span>34</span><span>></span><span>insert</span> <span>into</span> mytest(t2,t3,t4) <span>values</span>(<span>'</span><span>我们</span><span>'</span>,<span>'</span><span>他们</span><span>'</span>,<span>'</span><span>我们的</span><span>'</span><span>);
Query OK, </span><span>1</span> row affected (<span>0.00</span> sec)
登录后复制

MySQL InnoDB的存储结构总结

 ibd信息如下:

0000c070  73 75 70 72 65 6d 75 6d  <span>0a 02 02</span> <span>00</span> <span>00 00 10 00</span>  |supremum........|<span>
0000c080  </span><span>28</span> <span>80 00 00 01 00 00</span> <span>00  81 2d 27 80 00 00</span> <span>00 32</span>  |(........-'....2|<span>
0000c090  </span><span>01 10</span> 62 62 62 62 63 63  63 20 20 20 20 20 20 20  |..bbbbccc       |<span>
0000c0a0  0a </span>06 06 00 00 00 18 ff  c7 80 00 00 02 00 00 00  |................|<span>
0000c0b0  </span>81 2d 28 80 00 00 <span>00 32  01 10</span> <span>e6 88 91 e4 bb ac</span>  |.-(....2........|<span>
0000c0c0  <span>e4 bb </span></span><span>96 e4 bb ac</span> <span>e6 88  91 e4 bb ac e7 9a 84</span> 20  |............... |
登录后复制

因为表有了主键,所以ROWID(6字节)不见了。
特别注意的是:变长字段列表是3?表里面的varchar类型的列只有2个啊。经测试得出:在多字节字符集的条件下,char类型被当成可变长度的类型来处理,他们的行存储基本没有区别,所以这个就出现变长列表是3了,因为是utf8字符集,占用三个字节。所以一个汉字均占用了一个页中3个字节的空间(”我们“ :e6 88 91 e4 bb ac)。
数据列的信息:
id列的1值,应该是
80 00 00 01,为什么这个显示00 32 01 10,而且所有的id都是00 32 01 10。测试发现,id为自增主键的时候,id的4个字节长度都是以00 32 01 10 表示。否则和前面一个例子里说的,用select HEX(X) 表示。

总结2:
     上面的测试都是基于COMPACT存储格式的,不管是varchar还是char,NULL值是不需要占用存储空间的;特别需要注意的是Redumdant的记录头信息需要6个固定字节,而NULL值对于varchar来说是不需要占用存储空间,对于char来说将会占用最大值的字节数;在多字节字符集的条件下,CHAR和VARCHAR的行存储基本是没有区别的

来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板