MySQL Innodb 存储结构 和 存储Null值 用法详解
背景:
表空间: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
ViewCode #!/usr/bin/envpython #encoding=utf-8 importmylib fromsysimportargv frommylibimportmyargv if__name__=='__main__': myargv=myargv(argv) ifmyargv.parse_cmdline()==0: pass else: mylib.get_innodb_page_type(myargv)
mylib.py
ViewCode encoding=utf-8 importos importinclude fromincludeimport* TABLESPACE_NAME='D:\\mysql_data\\test\\t.ibd' VARIABLE_FIELD_COUNT=1 NULL_FIELD_COUNT=0 classmyargv(object): def__init__(self,argv): self.argv=argv self.parms={} self.tablespace='' defparse_cmdline(self): argv=self.argv iflen(argv)==1: print'Usage:pythonpy_innodb_page_info.py[OPTIONS]tablespace_file' print'Formoreoptions,usepythonpy_innodb_page_info.py-h' return0 whileargv: ifargv[0][0]=='-': ifargv[0][1]=='h': self.parms[argv[0]]='' argv=argv[1:] break ifargv[0][1]=='v': self.parms[argv[0]]='' argv=argv[1:] else: self.parms[argv[0]]=argv[1] argv=argv[2:] else: self.tablespace=argv[0] argv=argv[1:] ifself.parms.has_key('-h'): print'GetInnoDBPageInfo' print'Usage:pythonpy_innodb_page_info.py[OPTIONS]tablespace_file\n' print'Thefollowingoptionsmaybegivenasthefirstargument:' print'-hhelp' print'-ooutputputtheresulttofile' print'-tnumberthreadtoanaylethetablespacefile' print'-vverbosemode' return0 return1 defmach_read_from_n(page,start_offset,length): ret=page[start_offset:start_offset+length] returnret.encode('hex') defget_innodb_page_type(myargv): f=file(myargv.tablespace,'rb') fsize=os.path.getsize(f.name)/INNODB_PAGE_SIZE ret={} foriinrange(fsize): page=f.read(INNODB_PAGE_SIZE) page_offset=mach_read_from_n(page,FIL_PAGE_OFFSET,4) page_type=mach_read_from_n(page,FIL_PAGE_TYPE,2) ifmyargv.parms.has_key('-v'): ifpage_type=='45bf': page_level=mach_read_from_n(page,FIL_PAGE_DATA+PAGE_LEVEL,2) print"pageoffset%s,pagetype<%s>,pagelevel<%s>"%(page_offset,innodb_page_type[page_type],page_level) else: print"pageoffset%s,pagetype<%s>"%(page_offset,innodb_page_type[page_type]) ifnotret.has_key(page_type): ret[page_type]=1 else: ret[page_type]=ret[page_type]+1 print"Totalnumberofpage:%d:"%fsize fortypeinret: print"%s:%s"%(innodb_page_type[type],ret[type])
include.py
ViewCode #encoding=utf-8 INNODB_PAGE_SIZE=16*1024*1024 #Startofthedataonthepage FIL_PAGE_DATA=38 FIL_PAGE_OFFSET=4#pageoffsetinsidespace FIL_PAGE_TYPE=24#Filepagetype #Typesofanundologsegment*/ TRX_UNDO_INSERT=1 TRX_UNDO_UPDATE=2 #Onapageofanyfilesegment,datamaybeputstartingfromthisoffset FSEG_PAGE_DATA=FIL_PAGE_DATA #Theoffsetoftheundologpageheaderonpagesoftheundolog TRX_UNDO_PAGE_HDR=FSEG_PAGE_DATA PAGE_LEVEL=26#levelofthenodeinanindextree;theleaflevelisthelevel0*/ innodb_page_type={ '0000':u'FreshlyAllocatedPage', '0002':u'UndoLogPage', '0003':u'FileSegmentinode', '0004':u'InsertBufferFreeList', '0005':u'InsertBufferBitmap', '0006':u'SystemPage', '0007':u'TransactionsystemPage', '0008':u'FileSpaceHeader', '0009':u'扩展描述页', '000a':u'UncompressedBLOBPage', '000b':u'1stcompressedBLOBPage', '000c':u'SubsequentcompressedBLOBPage', '45bf':u'B-treeNode' } innodb_page_direction={ '0000':'Unknown(0x0000)', '0001':'PageLeft', '0002':'PageRight', '0003':'PageSameRec', '0004':'PageSamePage', '0005':'PageNoDirection', 'ffff':'Unkown2(0xffff)' } INNODB_PAGE_SIZE=1024*16#InnoDBPage16K
测试1:
root@localhost:test02:26:13>createtablett(idintauto_increment,namevarchar(10),ageint,addressvarchar(20),primarykey(id))engine=innodb; QueryOK,0rowsaffected(0.17sec) root@zhoujy:/var/lib/mysql/test#ls-lhtt.ibd -rw-rw----1mysqlmysql96K2012-10-1714:26tt.ibd
查看ibd:
root@zhoujy:/home/zhoujy/jiaoben/read_ibd#pythonpy_innodb_page_info.py/var/lib/mysql/test/tt.ibd-v pageoffset00000000,pagetypepageoffset00000001,pagetype pageoffset00000002,pagetype pageoffset00000003,pagetype ,pagelevel<0000>---叶子节点 pageoffset00000000,pagetype pageoffset00000000,pagetype Totalnumberofpage:6: FreshlyAllocatedPage:2 InsertBufferBitmap:1 FileSpaceHeader:1 B-treeNode:1 FileSegmentinode:1
解释:
Totalnumberofpage:总页数
FreshlyAllocatedPage:可用页
InsertBufferBitmap:插入缓存位图页
InsertBufferFreeList:插入缓存空闲列表页
B-treeNode:数据页
UncompressedBLOBPage:二进制大对象页,存放溢出行的页,即溢出页
上面得到的信息是表初始化大小为96K,他是有Totalnumberofpage*16得来的。1个数据页,2个可用页面。
root@localhost:test02:42:58>insertintottvalues(name,age,address)values('aaa',23,'HZZZ');
疑惑:为什么没有申请区?区是64个连续的页,大小1M。那么表大小也应该是至少1M。但是现在只有96K(默认)。原因是因为每个段开始的时候,先有32个页大小的碎片页存放数据,使用
完之后才是64页的连续申请,最多每次可以申请4个区,保证数据的顺序。这里看出表大小增加是按照至少64页的大小的空间来增加的,即1M增加。
验证:
填充数据,写满这32个碎片页,32*16=512K。看看是否能申请大于1M的空间。
ViewCode root@zhoujy:/home/zhoujy/jiaoben/read_ibd#ls-lh/var/lib/mysql/test/tt.ibd -rw-rw----1mysqlmysql576K2012-10-1715:30/var/lib/mysql/test/tt.ibd root@zhoujy:/home/zhoujy/jiaoben/read_ibd#pythonpy_innodb_page_info.py/var/lib/mysql/test/tt.ibd-v pageoffset00000000,pagetypepageoffset00000001,pagetype pageoffset00000002,pagetype pageoffset00000003,pagetype ,pagelevel<0001> pageoffset00000004,pagetype ,pagelevel<0000> pageoffset00000005,pagetype ,pagelevel<0000> pageoffset00000006,pagetype ,pagelevel<0000> pageoffset00000007,pagetype ,pagelevel<0000> pageoffset00000008,pagetype ,pagelevel<0000> pageoffset00000009,pagetype ,pagelevel<0000> pageoffset0000000a,pagetype ,pagelevel<0000> pageoffset0000000b,pagetype ,pagelevel<0000> pageoffset0000000c,pagetype ,pagelevel<0000> pageoffset0000000d,pagetype ,pagelevel<0000> pageoffset0000000e,pagetype ,pagelevel<0000> pageoffset0000000f,pagetype ,pagelevel<0000> pageoffset00000010,pagetype ,pagelevel<0000> pageoffset00000011,pagetype ,pagelevel<0000> pageoffset00000012,pagetype ,pagelevel<0000> pageoffset00000013,pagetype ,pagelevel<0000> pageoffset00000014,pagetype ,pagelevel<0000> pageoffset00000015,pagetype ,pagelevel<0000> pageoffset00000016,pagetype ,pagelevel<0000> pageoffset00000017,pagetype ,pagelevel<0000> pageoffset00000018,pagetype ,pagelevel<0000> pageoffset00000019,pagetype ,pagelevel<0000> pageoffset0000001a,pagetype ,pagelevel<0000> pageoffset0000001b,pagetype ,pagelevel<0000> pageoffset0000001c,pagetype ,pagelevel<0000> pageoffset0000001d,pagetype ,pagelevel<0000> pageoffset0000001e,pagetype ,pagelevel<0000> pageoffset0000001f,pagetype ,pagelevel<0000> pageoffset00000020,pagetype ,pagelevel<0000> pageoffset00000021,pagetype ,pagelevel<0000> pageoffset00000022,pagetype ,pagelevel<0000> pageoffset00000023,pagetype ,pagelevel<0000> Totalnumberofpage:36: InsertBufferBitmap:1 FileSpaceHeader:1 B-treeNode:33 FileSegmentinode:1
"额外"页:4个
pageoffset00000000,pagetype
pageoffset00000001,pagetype
pageoffset00000002,pagetype
pageoffset00000003,pagetype
碎片页:32个
pagetype
总共36个页,ibd大小576K的由来:32*16=512K(碎片页)+4*16=64(额外页),这里开始要是再插入的话,应该申请最少1M的页:
root@zhoujy:/home/zhoujy/jiaoben/read_ibd#ls-lh/var/lib/mysql/test/tt.ibd -rw-rw----1mysqlmysql2.0M2012-10-1716:10/var/lib/mysql/test/tt.ibd root@zhoujy:/home/zhoujy/jiaoben/read_ibd#pythonpy_innodb_page_info.py/var/lib/mysql/test/tt.ibd Totalnumberofpage:128: FreshlyAllocatedPage:91 InsertBufferBitmap:1 FileSpaceHeader:1 B-treeNode:34 FileSegmentinode:1
页从36跳到了128,因为已经用完了32个碎片页,新的页会采用区的方式进行空间申请。信息中看到有很多可用页,正好说明这点。
▲溢出行数据存放:INNODB存储引擎是索引组织的,即每页中至少有两行记录,因此如果页中只能存放一行记录,INNODB会自动将行数据放到溢出页中。当发生溢出行的时候,实际数据保存在BLOB页中,数据页只保存数据的前768字节(老的文件格式),新的文件格式(Barracuda)采用完全行溢出的方式,数据页只保存20个字节的指针,BLOB也保存所有数据。如何查看表中有溢出行数据呢?
root@localhost:test04:52:34>createtablet1(idint,namevarchar(10),memovarchar(8000))engine=innodbdefaultcharsetutf8; QueryOK,0rowsaffected(0.16sec) root@localhost:test04:53:10>insertintot1values(1,'zjy',repeat('我',8000)); QueryOK,1rowaffected(0.00sec)
查看ibd:
root@zhoujy:/home/zhoujy/jiaoben/read_ibd#pythonpy_innodb_page_info.py/var/lib/mysql/test/t1.ibd-v pageoffset00000000,pagetypepageoffset00000001,pagetype pageoffset00000002,pagetype pageoffset00000003,pagetype ,pagelevel<0000> pageoffset00000004,pagetype pageoffset00000005,pagetype Totalnumberofpage:6: InsertBufferBitmap:1 UncompressedBLOBPage:2 FileSpaceHeader:1 B-treeNode:1 FileSegmentinode:1
从信息中看到,刚才插入的一行记录,已经溢出了,保存到了2个BLOB页中(
root@localhost:test05:08:39>createtablet2(idint,namevarchar(1000),addressvarchar(512),companyvarchar(200),xxvarchar(512),memovarchar(512),demvarchar(1000))engine=innodbdefaultcharsetutf8; QueryOK,0rowsaffected(0.17sec) root@localhost:test05:08:43>insertintot2values(1,repeat('周',1000),repeat('我',500),repeat('丁',500),repeat('啊',500),repeat('噢',500),repeat('阿a',500));
1000+500+500+500+500+500=3500*3>8000字节;行会被溢出:
root@zhoujy:/home/zhoujy/jiaoben/read_ibd#pythonpy_innodb_page_info.py/var/lib/mysql/test/t2.ibd-v pageoffset00000000,pagetypepageoffset00000001,pagetype pageoffset00000002,pagetype pageoffset00000003,pagetype ,pagelevel<0000> pageoffset00000004,pagetype pageoffset00000000,pagetype Totalnumberofpage:6: InsertBufferBitmap:1 FreshlyAllocatedPage:1 FileSegmentinode:1 B-treeNode:1 FileSpaceHeader:1 UncompressedBLOBPage:1
root@zhoujy:/home/zhoujy/jiaoben/read_ibd#hexdump-C-v/var/lib/mysql/test/t1.ibd>t1.txt
查看ibd:
ViewCode 30820000c09000320110800000017a6a79e68891e688|.2......zjy.....| 30830000c0a091e68891e68891e68891e68891e68891|................| 30840000c0b0e68891e68891e68891e68891e68891e6|................| 30850000c0c08891e68891e68891e68891e68891e688|................| 30860000c0d091e68891e68891e68891e68891e68891|................| 30870000c0e0e68891e68891e68891e68891e68891e6|................| 30880000c0f08891e68891e68891e68891e68891e688|................| 30890000c10091e68891e68891e68891e68891e68891|................| 30900000c110e68891e68891e68891e68891e68891e6|................| 30910000c1208891e68891e68891e68891e68891e688|................| 30920000c13091e68891e68891e68891e68891e68891|................| 30930000c140e68891e68891e68891e68891e68891e6|................| 30940000c1508891e68891e68891e68891e68891e688|................| 30950000c16091e68891e68891e68891e68891e68891|................| 30960000c170e68891e68891e68891e68891e68891e6|................| 30970000c1808891e68891e68891e68891e68891e688|................| 30980000c19091e68891e68891e68891e68891e68891|................| 30990000c1a0e68891e68891e68891e68891e68891e6|................| 31000000c1b08891e68891e68891e68891e68891e688|................| 31010000c1c091e68891e68891e68891e68891e68891|................| 31020000c1d0e68891e68891e68891e68891e68891e6|................| 31030000c1e08891e68891e68891e68891e68891e688|................| 31040000c1f091e68891e68891e68891e68891e68891|................| 31050000c200e68891e68891e68891e68891e68891e6|................| 31060000c2108891e68891e68891e68891e68891e688|................| 31070000c22091e68891e68891e68891e68891e68891|................| 31080000c230e68891e68891e68891e68891e68891e6|................| 31090000c2408891e68891e68891e68891e68891e688|................| 31100000c25091e68891e68891e68891e68891e68891|................| 31110000c260e68891e68891e68891e68891e68891e6|................| 31120000c2708891e68891e68891e68891e68891e688|................| 31130000c28091e68891e68891e68891e68891e68891|................| 31140000c290e68891e68891e68891e68891e68891e6|................| 31150000c2a08891e68891e68891e68891e68891e688|................| 31160000c2b091e68891e68891e68891e68891e68891|................| 31170000c2c0e68891e68891e68891e68891e68891e6|................| 31180000c2d08891e68891e68891e68891e68891e688|................| 31190000c2e091e68891e68891e68891e68891e68891|................| 31200000c2f0e68891e68891e68891e68891e68891e6|................| 31210000c3008891e68891e68891e68891e68891e688|................| 31220000c31091e68891e68891e68891e68891e68891|................| 31230000c320e68891e68891e68891e68891e68891e6|................| 31240000c3308891e68891e68891e68891e68891e688|................| 31250000c34091e68891e68891e68891e68891e68891|................| 31260000c350e68891e68891e68891e68891e68891e6|................| 31270000c3608891e68891e68891e68891e68891e688|................| 31280000c37091e68891e68891e68891e68891e68891|................| 31290000c380e68891e68891e68891e68891e68891e6|................| 31300000c3908891e68891e68891e688910000021c00|................|
文本中刚好是48行,每行16字节。48*16=768字节,刚好验证了之前说的:数据页只保存数据的前768字节(老的文件格式)。
总结1:
通过上面的信息,可以能清楚的知道ibd表空间各个页的分布和利用信息以及表空间大小增加的步长;特别注意的是溢出行,一个页中至少包含2行数据,如果页中存放的行数越多,性能就越好。
************************************
************************************
目的2:
了解表空间如何存储数据,以及对NULL值的存储。
测试2:
在测试前先了解INNODB的存储格式(row_format)。老格式(Antelope):Compact<默认>,Redumdant;新格式(Barracuda):Compressed,Dynamic。
这里测试指针对默认的存储格式。
Compact行记录方式如下:
|变长字段长度列表(1~2字节)|NULL标志位(1字节)|记录头信息(5字节)|RowID(6字节)|事务ID(6字节)|回滚指针(7字节)|
上面信息除了"NULL标志位"[表中所有字段都定义为NOTNULL],"RowID"[表中有主键],"变长字段长度列表"[没有变长字段]可能不存在外,其他信息都会出现。所以一行数据除了列数据所占用的字段外,还需要额外18字节。
一:字段全NULL
mysql>createtablemytest(t1varchar(10),t2varchar(10),t3varchar(10),t4varchar(10))engine=innodbcharset=latin1row_format=compact; QueryOK,0rowsaffected(0.08sec) mysql>insertintomytestvalues('a','bb','bb','ccc'); QueryOK,1rowaffected(0.02sec) mysql>insertintomytestvalues('a','ee','ee','fff'); QueryOK,1rowaffected(0.01sec) mysql>insertintomytestvalues('a',NULL,NULL,'fff'); QueryOK,1rowaffected(0.00sec)
测试数据准备完之后,执行shell命令:
root@zhoujy:/usr/local/mysql/test#hexdump-C-vmytest.ibd>/home/zhoujy/mytest.txt
打开mytest.txt文件找到supremum这一行:
0000c07073757072656d756d0302020100000010|supremum........|----------->一行,16字节 0000c080002500000003b9000000000249018200|.%..........I...| 0000c09000014a01106162626262636363030202|..J..abbbbccc...| 0000c0a00100000018002300000003b901000000|......#.........| 0000c0b0024902830000014b0110616565656566|.I.....K..aeeeef| 0000c0c06666030106000020ffa600000003b902|ff.............| 0000c0d0000000024903840000014c0110616666|....I.....L..aff| 0000c0e066000000000000000000000000000000|f...............|
解释:
第一行数据:
03020201/*变长字段*/----表中4个字段类型为varchar,并且没有NULL数据,而且每个字段君小于255。
00/*NULL标志位,第一行没有null的数据*/
0000100025/*记录头信息,固定5个字节*/
00000003b900/*RowID,固定6个字节,表没有主键*/
000000024901/*事务ID,固定6个字节*/
820000014a0110/*回滚指针,固定7个字节*/
6162626262636363/*列的数据*/
第二行数据和第一行数据一样(颜色匹配)。
第三行数据(有NULL值)和第一行的解释的颜色对应起来比较差别:
03020201VS0301----------当值为NULL时,变长字段列表不会占用存储空间。 6162626262636363VS61666666---------NULL值没有存储,不占空间
结论:当值为NULL时,变长字段列表不会占用存储空间。NULL值没有存储,不占空间,但是需要一个标志位(一行一个)。
二:字段全NOTNULL
mysql>createtablemytest(t1varchar(10)NOTNULL,t2varchar(10)NOTNULL,t3varchar(10)NOTNULL,t4varchar(10)NOTNULL)engine=innodbcharset=latin1row_format=compact; QueryOK,0rowsaffected(0.03sec) mysql>insertintomytestvalues('a','bb','bb','ccc'); QueryOK,1rowaffected(0.01sec) mysql>insertintomytestvalues('a','ee','ee','fff'); QueryOK,1rowaffected(0.01sec) mysql>insertintomytestvalues('a',NULL,NULL,'fff'); ERROR1048(23000):Column't2'cannotbenull
步骤和上面一样,得到的ibd的结果是:
0000c07073757072656d756d0302020100001000|supremum........| 0000c0802400000003b903000000024907870000|$..........I....| 0000c090014f0110616262626263636303020201|.O..abbbbccc....| 0000c0a0000018ffcb00000003b9040000000249|...............I| 0000c0b008880000015001106165656565666666|.....P..aeeeefff|
和上面比较,发现少了NULL的标志位信息。
结论:NULL值会有额外的空间来存储,即每行1字节的大小。对于相同数据的表,字段中有NULL值的表比NOTNULL的大。
三:1个NULL,和1个''的数据:
mysql>createtablemytest(t1varchar(10)NOTNULL,t2varchar(10)NOTNULLDEFAULT'',t3varchar(10)NOTNULL,t4varchar(10))engine=innodbcharset=latin1row_format=compact; QueryOK,0rowsaffected(0.02sec) mysql>insertintomytest(t1,t2)values('A','BB'); QueryOK,1rowaffected,1warning(0.01sec)
步骤和上面一样,得到的ibd的结果是:
0000c07073757072656d756d00020101000010ff|supremum........| 0000c080ef00000043b903000000024a15900000|....C......J....| 0000c09001c20110414242000000000000000000|....ABB.........|
和上面2个区别主要在于变长列表和列数据这里。
结论:列数据信息里表明了NULL数据和''数据都不占用任何空间,对于变长字段列表的信息,和一对比得出:‘'数据虽然不需要占用任何存储空间,但是在变长字段列表里面还是需要占用一个字节<毕竟还是一个‘'值>,NULL值不需要占用”,只是NULL会有额外的一个标志位,所以能有个优化的说法:“数据库表中能设置NOTNULL的就尽量设置为NOTNULL,除非确实需要NULL值得。”在此得到了证明。
上面的测试都是针对VARCHAR的变长类型,那对于CHAR呢?
CHAR测试:
root@localhost:test10:33:35>createtablemytest(t1char(10),t2char(10),t3char(10),t4char(10))engine=innodbcharset=latin1row_format=compact;QueryOK,0rowsaffected(0.16sec) root@localhost:test10:33:59>insertintomytestvalues('a','bb','bb','ccc'); QueryOK,1rowaffected(0.00sec) root@localhost:test10:34:09>insertintomytestvalues('a','ee','ee','fff'); QueryOK,1rowaffected(0.00sec) root@localhost:test10:34:19>insertintomytestvalues('a',NULL,NULL,'fff'); QueryOK,1rowaffected(0.00sec)
打开ibd生成的文件:
0000c06002001b696e66696d756d0004000b0000|...infimum......| 0000c07073757072656d756d0000001000410000|supremum.....A..| 0000c080000af500000000812d07800000003201|........-.....2.| 0000c09010612020202020202020206262202020|.abb| 0000c0a020202020206262202020202020202063|bbc| 0000c0b063632020202020202000000018004100|cc.....A.| 0000c0c000000af501000000812d088000000032|.........-.....2| 0000c0d001106120202020202020202065652020|..aee| 0000c0e020202020202065652020202020202020|ee| 0000c0f06666662020202020202006000020ff70|fff....p| 0000c1000000000af502000000812d0980000000|..........-.....| 0000c11032011061202020202020202020666666|2..afff| 0000c12020202020202020000000000000000000|.........|
和一的varchar比较发现:少了变长字段列表,但是对于char来讲,需要固定长度来存储的,存不到固定长度,也会被填充满。如:20;并且NULL值也不需要占用存储空间。
混合(varchar,char):
root@localhost:test11:21:48>createtablemytest(t1int,t2char(10),t3varchar(10),t4char(10))engine=innodbcharset=latin1row_format=compact; QueryOK,0rowsaffected(0.17sec) root@localhost:test11:21:50>insertintomytestvalues(1,'a','b','c'); QueryOK,1rowaffected(0.00sec) root@localhost:test11:22:06>insertintomytestvalues(11,'aa','bb','cc'); QueryOK,1rowaffected(0.00sec)
从上面的表结构中看出:
1,变长字段列表长度:1
2,NULL标志位:1
3,记录头信息:5
4,RowID:6
5,事务ID:6
6,回滚指针:7
idb的信息:
0000c07073757072656d756d0100000010003300|supremum......3.| 0000c08000000af507000000812d1a8000000032|.........-.....2| 0000c09001108000000161202020202020202020|......a| 0000c0a062632020202020202020200200000018|bc.....| 0000c0b0ffbe0000000af508000000812d1b8000|............-...| 0000c0c000003201108000000b61612020202020|..2......aa| 0000c0d020202062626363202020202020202000|bbcc.|
从上信息得出和之前预料的一样:因为表中只有一个varchar字段,所以,变长列表长度就只有:01
特别注意的是:各个列数据存储的信息:t1字段为int类型,占用4个字节的大小。第一行:80000001就是表示1数字;第二行:8000000b表示了11的数字。[selecthex(11)==B],其他的和上面的例子一样。
上面都是latin1单字节字符集的说明,那对于多字节字符集的情况怎么样?
root@localhost:test11:52:10>createtablemytest(idintauto_increment,t2varchar(10),t3varchar(10),t4char(10),primarykey(id))engine=innodbcharset=utf8row_format=compact; QueryOK,0rowsaffected(0.17sec) root@localhost:test11:52:11>insertintomytest(t2,t3,t4)values('bb','bb','ccc'); QueryOK,1rowaffected(0.00sec) root@localhost:test11:55:34>insertintomytest(t2,t3,t4)values('我们','他们','我们的'); QueryOK,1rowaffected(0.00sec)
ibd信息如下:
0000c07073757072656d756d0a02020000001000|supremum........| 0000c0802880000001000000812d278000000032|(........-'....2| 0000c09001106262626263636320202020202020|..bbbbccc| 0000c0a00a060600000018ffc780000002000000|................| 0000c0b0812d2880000000320110e68891e4bbac|.-(....2........| 0000c0c0e4bb96e4bbace68891e4bbace79a8420|...............|
因为表有了主键,所以ROWID(6字节)不见了。
特别注意的是:变长字段列表是3?表里面的varchar类型的列只有2个啊。经测试得出:在多字节字符集的条件下,char类型被当成可变长度的类型来处理,他们的行存储基本没有区别,所以这个就出现变长列表是3了,因为是utf8字符集,占用三个字节。所以一个汉字均占用了一个页中3个字节的空间(”我们“:e68891e4bbac)。
数据列的信息:
id列的1值,应该是80000001,为什么这个显示00320110,而且所有的id都是00320110。测试发现,id为自增主键的时候,id的4个字节长度都是以00320110表示。否则和前面一个例子里说的,用selectHEX(X)表示。
总结2:
上面的测试都是基于COMPACT存储格式的,不管是varchar还是char,NULL值是不需要占用存储空间的;特别需要注意的是Redumdant的记录头信息需要6个固定字节;在多字节字符集的条件下,CHAR和VARCHAR的行存储基本是没有区别的。
到此这篇关于MySQLInnodb存储结构和存储Null值用法详解的文章就介绍到这了,更多相关MySQLInnodb存储结构 存储Null值内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。