同志们,出现了一个有趣的问题,也许有人能告诉我答案。
背景:有一个大小合适的表,大部分数据被一个 JSON 列占据。我需要以某种方式收紧空间,所以我正在做一些“研究”。
在本地扩展了约 130 万行的表片段。我们有如下表结构(InnoDB,row_format=DYNAMIC
):
a BIGINT,
b BIGINT,
c TINYINT,
d JSON NULL,
PRIMARY KEY (a,b,c)
这会占用5.8 GB磁盘空间(所有 JSON 字段均非空)。
- 如果我们加起来,
JSON_STORAGE_SIZE(d)
我们得到~3.5GB。 - 我们还可以假设所需的容量约为
a,b,c = (8+8+1)*1300000/1024/1024
20MB
“理论”和实际大小之间2GB 的差异看起来还不错。
为了实验我做了两个表:
(a,b,c)
没有 json 列的同一张表。大小48MB(i,d)
,其中 i -INT AUTO_INCREMENT
(即将密钥简化为整数) - 大小4.8 GB。
这里,这两个表的总大小比原始表小1 GB(尽管实际上INT
向数据中添加了一列)。
谁能解释一下造成这种体积差异的原因是什么?
PS:如果你有兴趣,它row_format=compressed
比原来的 5.8 -> 2.4
PS2:仅创建表。即数据已创建并上传。没有进行任何删除/更新
我有一种感觉,答案将包括有关集群、页面等的内容。但在物理存储机制方面不强。
MySQL 不具备物理存储功能,不会因不同列类型的组合而导致数据量显着增大。任何大小差异只能由表中剩余的未使用空间引起。当您删除记录或将记录移动到新位置时,仍保留可用空间。
InnoDB 格式涉及通过主键对表进行聚类。术语“集群”隐藏了索引树节点中所有数据的存储。存储时,为主键建立索引,并将数据排序到其中。具有相似主键值的记录物理上位于相同的页面上。
将数据加载到表中时,如果新记录以随机顺序(相对于主键)到达,MySQL 需要将现有记录移动到新位置,以便在按键靠近它们的记录旁边插入新记录。此外,需要更频繁地平衡树。为了减少插入过程中记录的移动,MySQL 为将来的数据预留了页空间。当以随机顺序插入时,块中会留下更多保留区域。所有这些都会导致表中有大量未使用的空间。
在问题描述的情况下,表大小的差异不是由明显更有用的数据引起的,而是由插入数据的条件不同引起的。在一种情况下,数据相对于主键的到达是混乱的;在另一种情况下,主键基于自动增量并且单调增长。这导致第一种情况下的数据移动过多,而第二种情况下的数据移动量要少得多。