表空间可以看成innodb逻辑存储的最高层,默认会有一个共享的表空间ibdata1,所有的数据都存放在这个表空间内,如果启用了innodb_file_pre_table,则每张表都会单独放在一个表空间中。但是有些数据是一定会放在ibdata1中的,例如回滚信息(undo),插入缓存索引页,事务信息等。
表空间是由段组成的,例如数据段,索引段,回滚段。数据段就是B+数的叶子节点,索引段就是B+数的非叶子节点,段的管理都是引擎自己操作的,DBA不能操作。
区是由连续的页组成的,任何情况下区的大小都是1M,为保证区中的页是连续的,innodb一次从磁盘申请4-5个区,默认情况下,一个页大小是16k,索引一个区有64个连续的页。如果设置了innodb_file_pre_table,刚开始插入数据时,并不会直接把数据插入到这个连续的页中,而是写入到32个碎片页。因为申请一个区要1M,而有些很小的表用不到64个页。
有些人称为块,是innodb磁盘管理的最小单位,默认是16k,可以通过innodb_page_size设置为4k, 8k, 16k。
常见的页的类型:
数据页(B-tree Node)
undo页(undo log page)
系统页(System page)
事务数据页(Transaction system page)
插入缓冲位图页
插入缓冲空闲列表页
未压缩的二进制大对象页
压缩的二进制大对象页
页的结构由7部分组成
1.File Header(文件头)
2.Page Header(页头)
3.Infimun和Supremun Records
4.User Record
5.Free Space
6.Page Directory(页目录)
7.File Trailter(文件结尾信息)
Innodb是面向行的(row-oriented),也就是说数据是一行一行存放的,一页可以放16k/2-200,最多7992行记录。Innodb行记录格式在mysql5.1版本后,默认是compact格式,可以通过命令,SHOW TABLE STATUS LIKE 'table_name'查看当前的行结构,
1.放varchar列的长度,顺序与列倒序,列的长度小于255,用一个字节存,大于255,用两个字节存,varchar最长长度不超过65535字节
2, null标志位,固定1字节,指示改行数据是否有null值
3.头信息,固定5字节
4.每个实际的列数据,如果该列为null,则不分配
5.除了表自定义的列,还有两个隐藏列,事务id(6字节)和回滚指针列(7字节),如果没有定义主键,还会有一个rowid列(6字节)。
需要注意的是,我们创建表是 varchar(50),char(50),这个50指的是字符,而varchar总长度不能超过65535个字节 。但是innodb一个页最大是16k,16384个字节,如果超出就会发生行溢出,
实际上innodb每个页至少要放2个行,如果放不下,就会自动把行数据放到溢出页(Blob page)中;而对于text和Blob类数据,也不一定就放到Blob page中,还是要判断一个页是否可以放两条记录。
先假设一行数据大小为1k,一页大小为16k。
计算一个B+树的叶子节点,一页能放16k/1k=16行记录。
一个B+树非叶子节点,放主键的值和一个指针,假设主键为bigint(8bit),指针大小6字节,一共14字节,
16k/14 = 1170, 一个非叶子节点可以放1170个指针。
如果树高为2,总共可以放 1170*16 = 18720 行数据
如果树高为3,总共可以放1170*1170*16 = 21902400 ,约2千万行数据
所以主键越小,每个非叶子节点放的索引就越多。树高为3的B+,查询一个数据只要3次IO即可。
https://cloud.tencent.com/developer/article/1494725