为什么InnoDB表的大小远大于预期?

时间:2010-05-26 15:41:31

标签: mysql innodb

我正试图找出不同存储引擎的存储要求。我有这张桌子:

CREATE TABLE  `mytest` (
  `num1` int(10) unsigned NOT NULL,
  KEY `key1` (`num1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

当我插入一些值然后运行show table status;时,我得到以下内容:

+----------------+--------+---------+------------+---------+----------------+-------------+------------------+--------------+-----------+----------------+---------------------+---------------------+------------+-------------------+----------+----------------+---------+
| Name           | Engine | Version | Row_format | Rows    | Avg_row_length | Data_length | Max_data_length  | Index_length | Data_free | Auto_increment | Create_time         | Update_time         | Check_time | Collation         | Checksum | Create_options | Comment |
+----------------+--------+---------+------------+---------+----------------+-------------+------------------+--------------+-----------+----------------+---------------------+---------------------+------------+-------------------+----------+----------------+---------+
| mytest         | InnoDB |      10 | Compact    | 1932473 |             35 |    67715072 |                0 |     48840704 |   4194304 |           NULL | 2010-05-26 11:30:40 | NULL                | NULL       | latin1_swedish_ci |     NULL |                |         |

注意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字节的无符号int时需要这么大的avg_row_length?

4 个答案:

答案 0 :(得分:10)

InnoDB表是群集的,这意味着所有数据都包含在B-Tree中,其中PRIMARY KEY作为键,所有其他列作为有效负载。

由于您没有定义显式PRIMARY KEYInnoDB使用隐藏的6字节列对记录进行排序。

B-Tree组织的这个和开销(带有额外的非叶级别块)需要比sizeof(int) * num_rows更多的空间。

答案 1 :(得分:2)

以下是您可能会发现有用的更多信息。

InnoDB以16KB页面的形式分配数据,因此如果您只有几行并且表格是< SHOW TABLE STATUS“将为行大小提供膨胀数字。总共16K。 (例如,有4行,平均行大小返回为4096.)

当“空间”是一个重要考虑因素时,“不可见”主键每行额外增加6个字节是一个关键点。如果您的表只有一列,那么这是制作主键的理想列,假设其中的值是唯一的:

CREATE TABLE `mytest2`
       (`num1` int(10) unsigned NOT NULL primary key)
ENGINE=InnoDB DEFAULT CHARSET=latin1;

通过使用这样的PRIMARY KEY:

  1. 不需要INDEX或KEY子句,因为您没有辅助索引。 InnoDB表的索引组织格式使您可以基于主键值免费快速查找。
  2. 您最终不会使用NUM1列数据的另一个副本,这是明确索引该列时会发生的情况。
  3. 您不会使用另一个6字节不可见主键值的副本。主键值在每个二级索引中重复。 (这也是你可能不希望10个列的表上有10个索引的原因,你可能不想要一个组合了几个不同列的主键或者是一个长字符串列。)
  4. 总的来说,只使用主键意味着与表+索引相关的数据更少。为了了解整体数据大小,我喜欢用

    运行
    set innodb_file_per_table = 1;
    

    并检查data / 数据库 / * table * .ibd文件的大小。每个.ibd文件都包含InnoDB表及其所有相关索引的数据。

    为了快速构建一个用于测试的大表,我通常会运行如下语句:

    insert into mytest
    select * from mytest;
    

    每次数据量增加一倍。对于使用主键的单列表,由于值必须是唯一的,我使用变体来保持值不会相互冲突:

    insert into mytest2
    select num1 + (select count(*) from mytest2) from mytest2;
    

    通过这种方式,我可以将平均行大小降低到25.空间开销基于您希望使用指针式机制快速查找单个行的基本假设,并且大多数表将具有除了具有可以求和,平均和显示的实际数据的列之外,其值用作指针(即主键)的列。

答案 2 :(得分:1)

除了Quassnoi的非常好的答案之外,您应该尝试使用重要的数据集。

我要做的是,加载1M行模拟生产数据,然后测量表格大小并将其用作指南。

这就是 I've done in the past anyway

答案 3 :(得分:0)

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与DYNAMIC行-当表为FIXED大小时,“指针”是行号。对于DYNAMIC,它是.MYD的字节偏移。

  • 索引条目还必须使用指针指向数据行。因此,您的ALTER也应该缩小.MYI文件!

还有更多细节,但是MyISAM可能会消失,因此,任何人都不会担心这种古老的历史。

InnoDB

https://stackoverflow.com/a/64417275/1766831