为啥 InnoDB 表大小比预期大得多?
Posted
技术标签:
【中文标题】为啥 InnoDB 表大小比预期大得多?【英文标题】:Why is InnoDB table size much larger than expected?为什么 InnoDB 表大小比预期大得多? 【发布时间】:2011-02-24 06:18:37 【问题描述】:我正在尝试找出不同存储引擎的存储要求。我有这张桌子:
CREATE TABLE `mytest` (
`num1` int(10) unsigned NOT NULL,
KEY `key1` (`num1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
当我插入一些值然后运行 show table status;
时,我得到以下信息:
注意 avg_row_length 是 35。我很困惑,当我只存储一个不可为空的整数时,InnoDB 不会更好地利用空间。
我在 myISAM 上运行了相同的测试,默认情况下 myISAM 在此表上每行使用 7 个字节。当我跑步时
ALTER TABLE mytest MAX_ROWS=50000000, AVG_ROW_LENGTH = 4;
使 myISAM 最终正确使用 5 字节行。
当我为 InnoDB 运行相同的 ALTER TABLE 语句时,avg_row_length 不会改变。
为什么只存储 4 字节的 unsigned int 时需要这么大的 avg_row_length?
【问题讨论】:
我刚刚读到 InnoDB 将表空间用于数据和索引。这是有道理的,似乎这就是我看到如此大的 avg_row_length 的原因......也许。我还发现每个叶子节点都存储了事务 ID 和回滚指针。好吧,我本身并没有使用交易,因此对这些数据没有用处。有没有办法不存储这些值?有什么方法可以使用 InnoDB,但可以更好地利用存储?谢谢! @alessandro:是的,事务支持也确实增加了一些开销。您没有使用事务这一事实并不意味着它们没有被使用:例如,在长时间的UPDATE
操作期间被杀死的线程将在InnoDB
中正确回滚,但在MyISAM
中不会正确回滚。事务支持是使用InnoDB
的重点,如果你不需要它,只需使用MyISAM
。
@Quassnoi:我的印象是 MyISAM 不像 InnoDB 那样“成熟”或生产就绪……也许这是一种毫无根据的恐惧。除了 MyISAM 需要全表锁来保证一致性之外,MyISAM 在备份数据库时是否会给表带来任何困难。我不需要交易,我将存储大量数据。 MyISAM 是否有任何已知问题会导致您不使用它?
@alessandro:“不需要交易”到底是什么意思? en.wikipedia.org/wiki/ACID 的哪一部分你不需要?
@alessandro:是的,我已经在生产环境中部署了数百个 MyISAM 数据库。至于提示,在不知道您的要求的情况下,在一篇文章中推荐的提示太多了。阅读mysqlperformanceblog.com 了解系统管理技巧,阅读我的博客explainextended.com/category/mysql,了解高效SQL
。
【参考方案1】:
MyISAM
MyISAM,除了在真正的旧版本中,使用一个 7 字节的“指针”来定位一行,并在索引中使用一个 6 字节的指针。这些默认值会导致 巨大 最大表大小。更多详情:http://mysql.rjweb.org/doc.php/limits#myisam_specific_limits。改变这些的笨拙方法涉及您发现的ALTER .. MAX_ROWS=50000000, AVG_ROW_LENGTH = 4
。服务器将这些值相乘以计算数据指针需要多少字节。因此,您偶然发现了如何缩小 avg_row_length。
但您实际上需要声明一个少于 7 个字节的表才能命中它!指针大小多处显示:
.MYD 中的可用空间链接默认为 7 个字节。因此,当您删除一行时,会提供一个指向下一个空闲位置的链接。该链接需要为 7 个字节(默认情况下),因此行大小是从 4 字节 INT 人为扩展来为其腾出空间的! (还有更多细节与列是否为 NULLable 等有关。
FIXED vs DYNAMIC row -- 当表是 FIXED 大小时,“指针”是一个行号。对于 DYNAMIC,它是 .MYD 中的字节偏移量。
索引条目还必须通过指针指向数据行。所以你的ALTER
应该也缩小了 .MYI 文件!
还有更多细节,但 MyISAM 很可能会消失,所以这段古老的历史不太可能引起任何人的关注。
InnoDB
https://***.com/a/64417275/1766831
【讨论】:
【参考方案2】:这里有一些您可能会觉得有用的更多信息。
InnoDB 以 16KB 页面的形式分配数据,因此如果您只有几行并且表的总大小小于 16K,那么“SHOW TABLE STATUS”将给出行大小的膨胀数字。 (例如,对于 4 行,平均行大小返回为 4096。)
当空间是一个重要的考虑因素时,“不可见”主键每行额外的 6 个字节是一个关键点。如果您的表只有一列,那么这是制作主键的理想列,假设其中的值是唯一的:
CREATE TABLE `mytest2`
(`num1` int(10) unsigned NOT NULL primary key)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
通过使用这样的主键:
-
不需要 INDEX 或 KEY 子句,因为您没有二级索引。 InnoDB 表的索引组织格式可让您免费根据主键值快速查找。
您不会得到另一个 NUM1 列数据的副本,当该列被显式索引时会发生这种情况。
您不会得到另一个 6 字节不可见主键值的副本。主键值在每个二级索引中重复。 (这也是为什么您可能不希望有 10 列的表上有 10 个索引,并且您可能不希望主键组合多个不同的列或者是一个长字符串列。)
因此,总的来说,仅使用主键意味着与表 + 索引关联的数据更少。为了了解整体数据大小,我喜欢使用
set innodb_file_per_table = 1;
并检查数据/数据库/*table*.ibd 文件的大小。每个 .ibd 文件都包含 InnoDB 表及其所有关联索引的数据。
为了快速建立一个大表进行测试,我通常会运行如下语句:
insert into mytest
select * from mytest;
每次都会使数据量翻倍。在使用主键的单列表的情况下,由于值必须是唯一的,我使用了一个变体来防止值相互冲突:
insert into mytest2
select num1 + (select count(*) from mytest2) from mytest2;
这样,我能够将平均行大小降低到 25。空间开销基于您希望使用指针式机制快速查找各个行的基本假设,并且大多数表将具有除了具有被求和、平均和显示的真实数据的列之外,其值用作指针(即主键)的列。
【讨论】:
【参考方案3】:除了 Quassnoi 的非常好的答案之外,您可能应该使用重要的数据集进行尝试。
我要做的是,加载 100 万行模拟生产数据,然后测量表大小并将其用作指导。
这就是 I've done in the past anyway
【讨论】:
是的,谢谢 MarkR,我用 50M 行的数据集完成了这项工作。 InnoDB 在 3GIG 上使用了 3 倍以上的空间 根据我的研究(见上面的链接),这是典型的;您可以通过使用插件并打开压缩来使用更少的空间。有关一些数据,请参阅我的博客文章。【参考方案4】:InnoDB
表是集群的,这意味着所有数据都包含在 B-Tree
中,PRIMARY KEY
作为键,所有其他列作为有效负载。
由于您没有明确定义 PRIMARY KEY
,InnoDB
使用隐藏的 6 字节列对记录进行排序。
B-Tree
组织的这个和开销(带有额外的非叶级块)需要比sizeof(int) * num_rows
更多的空间。
【讨论】:
我知道这个答案已经有 10 多年的历史了,但我仍然想知道。提到的与 OP 的差异是 30 字节。那么这是否意味着6
字节占用隐藏列,4
整数和剩余25
字节都属于B-Tree
? 71%
对B-Tree
来说是不是太过分了?
@Adam: avg_row_length
是页数 * 页面大小 / 记录数。页数(相对于记录数)取决于 B-Tree 深度和碎片。碎片发生在删除、不按 PK 顺序插入或更新会增加行大小(后两个操作导致页面拆分)。分页存储(页眉等)也会导致一些小的内在开销。如果您的 MERGE_THRESHOLD 足够低并且您正在对表进行大量删除,那么 71% 的开销是一个合理的值以上是关于为啥 InnoDB 表大小比预期大得多?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 iPhone .app 文件比 .ipa 文件大得多?
为啥我的 favicon.ico 文件比其各部分的总和大得多?
对象比相应的 .RData 文件大得多。为啥?可以手动做吗?