我有一个巨大的InnoDB表,有三列(int,mediumint,int)。 innodb_file_per_table
设置已启用,前两列只有PRIMARY KEY
表架构是:
CREATE TABLE `big_table` (
`user_id` int(10) unsigned NOT NULL,
`another_id` mediumint(8) unsigned NOT NULL,
`timestamp` int(10) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`another_id `)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
MySQL版本为5.6.16
目前我每秒多插入超过150行。没有删除,也没有更新。 没有重大的回滚或其他事务中止,这会导致浪费空间使用。
MySQL在该表上显示计算出的大小为75,7GB。
光盘上的.ibd大小:136,679,784,448字节(127.29 GiB)
计数行:2,901,937,966(每行47.10字节)
2天后,MySQL在该表上显示的计算大小为75.7 GB。
光盘上的.ibd大小:144,263,086,080字节(135.35 GiB)
计数行:2,921,284,863(每行49.38字节)
表格的运行SHOW TABLE STATUS
显示:
Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Collation
InnoDB | 10 | Compact | 2645215723 | 30 | 81287708672 | 0 | 0 | 6291456 | utf8_unicode_ci
以下是我的问题:
Avg_row_length
和Data_length
完全错误?希望有人可以帮助我,光盘的使用不会再像这样了。我没有注意到,因为桌子较小。
答案 0 :(得分:7)
我假设您的表有时没有增长到目前的~29亿行,并且您最近加载了这些数据或导致表重组(使用ALTER TABLE
或{{ 1}},例如)。所以它开始在磁盘上非常好。
根据您的表模式(幸运的是非常简单和直接),每行(记录)的布局如下:
OPTIMIZE TABLE
InnoDB实际上永远不会将页面填充到大约15/16满(通常不会少于1/2满)。在各个地方的所有额外开销中,记录的满载成本在索引的叶页中最小约32字节,每行最多60字节。
当您通过导入或通过(Header) 5 bytes
`user_id` 4 bytes
`another_id` 3 bytes
(Transaction ID) 6 bytes
(Rollback Pointer) 7 bytes
`timestamp` 4 bytes
=============================
Total 29 bytes
或ALTER TABLE
批量加载数据时,通常会按OPTIMIZE TABLE
按顺序加载数据(并创建索引),这允许InnoDB非常有效地将数据打包在磁盘上。如果随后继续以随机(或有效随机)顺序将数据写入表中,则必须扩展高效打包的索引结构以接受新数据,在B + Tree术语中,这意味着将页面拆分为一半。如果你有一个理想的16 KiB页面,其中记录平均消耗约32个字节,并且它被分成两半以插入一行,你现在有两个半空页面(约16 KiB浪费)并且新行有“费用”16 KiB。
当然,这不是真的。随着时间的推移,索引树会在页面介于1/2满和15/16之间的情况下稳定下来 - 它不会永久地分割页面,因为必须发生在同一页面中的下一个插入将发现已经有足够的空间存在进行插入。
如果您最初将数据批量加载(并因此有效地打包)到表中然后切换到有机增长它,这可能会有点令人不安。最初似乎桌子正以疯狂的速度增长,但如果你追踪增长率,它应该会放慢速度。
您可以在我的博文中详细了解InnoDB索引和记录布局:The physical structure of records in InnoDB,The physical structure of InnoDB index pages和B+Tree index structures in InnoDB。