问题描述:

同事在给jiradb做mysqldump时,发现dump出来的文件只有10MB左右,而ibd文件占用磁盘空间100MB左右。

最初,我们猜测可能是delete操作导致了大量的磁盘碎片,以及二级索引占用了很多空间。

但是对比了data_length+index_length+data_free的总和,与du的输出结果对比,还是相差较多。

版本信息:Server version: 5.6.48-log MySQL Community Server (GPL)

概念解释:

data_length:聚集索引所占用的空间,单位是bytes

index_length:二级索引所占用的空间,单位是bytes

data_free:已分配但是未使用的空间,单位是bytes

参考链接:https://dev.mysql.com/doc/refman/5.6/en/tables-table.html

分析过程:

1、首先,抽查占用空间最大的changeitem.ibd,du显示它占用磁盘11268KB

2、analyze table changeitem之后,查询information_schema.tables,得出2637824 + 589824 + 4194304 = 7,421,952 = 7248KB,与du显示的结果相差4020KB

3、使用py_innodb_page_info工具(原作者在https://code.google.com/archive/p/david-mysql-tools/中的原版已经找不到了,我在github上找到了其它版本,并做了一点小的修改,下载地址https://github.com/johnliu2008/py_innodb_page_info)分析ibd文件:

可以看到,B-tree Node有174个,这个数目是包含了聚集索引页和二级索引页的数量;这个工具的原理,是通过逐个块地扫描ibd文件,通过每个块的page_type值来判断属于什么类型的块,其中用到的innodb_page_type字典,与源码storage/innobase/include/fil0fil.h中的定义一致(题外话:8.0版本新增了SDI页类型值,姜大原版的工具不能支持,我添加了SDI页类型值的字典信息)

Freshly Allocated Page有527个,约‭8,634,368字节,比上一步中查出的data_free‬值要大很多,通过参照官档关于data_free的描述(Free space means the number of bytes in completely free extents minus a safety margin.),知道data_free只是空闲空间的一部分。我好奇的是:InnoDB表的data_free值为什么都是1MB的整数倍?safety margin指的是什么?后面会通过源码分析来解释。

4、看看我们最初的猜测,是不是delete导致了很多磁盘碎片空间呢?

a.通过官档对data_free的解释,可以知道data_free≠碎片空间的容量,而是一种完全空闲的空间

b.实验证明,仅仅insert,也会导致本文标题所描述的现象

因此,关于delete造成碎片空间的假设不成立。

源码中关于data_free的计算方法:

storage/innobase/handler/i_s.cc:i_s_files_table_fill()中,avail_space = fsp_get_available_space_in_free_extents(space());

接着看看fsp_get_available_space_in_free_extents()函数,在storage\innobase\fsp\fsp0fsp.cc中:

5、通过搜索,在Percona博客找到一篇关于如果计算InnoDB表占用磁盘空间的博文,不过是针对5.7以上版本的,不适用于5.6版本,记录下链接https://www.percona.com/blog/2016/01/26/finding_mysql_table_size_on_disk/,以供参考。

6、查看官档https://dev.mysql.com/doc/refman/5.6/en/innodb-file-per-table-tablespaces.html,其中描述ibd文件增长的步长是4MB,但是实际验证发现,当有足够多空闲空间的时候,ibd文件以小于4MB的步长增长,因为InnoDB分配磁盘空间以extent为单位,所以步长一定是1MB的整数倍:

那么,ibd文件的自增长时,为什么会有那么多的空闲空间呢?去看看源码中的fsp_reserve_free_extents()函数:

结论:

通过上面的分析,我们可以知道:

1、data_free≠碎片空间的容量,而是一种完全空闲的空间,大小是1MB的整数倍

2、ibd文件空闲空间<=4个extents也就是4MB时,就会尝试进行扩展

3、我在官档和源码中,没有找到ibd文件需要自动扩展的原因,但是结合工作经验,我猜测:表空间文件扩展时开销比较大,所以通过预先分配空间,以减少事务在写入时遇到空间不足而临时进行扩展的开销。以前的项目中使用Oracle数据库时,生产环境中的库都会预先把表空间设置得比较大,这样虽然会造成空间浪费,但是对性能友好,通俗地说,就是空间换时间。

05-13 01:38