【发布时间】:2021-07-07 14:05:02
【问题描述】:
我想解决的特殊问题是一个列非常宽(有时 > 1000 列)的表,这需要我拆分表。为了巧妙地做到这一点,我想在 Row size too large (> 8126) 或 所用表类型的最大行大小(不包括 BLOB)之前进行拆分,是 65535。.
为此,我试图找出每列的实际大小。有些很简单(如 BOOLEAN、INT、DATE、TEXT 等)。我想弄清楚VARCHAR。因此,创建具有许多 VARCHAR 列的表的实验。
但是,我无法真正理解它。我尝试使用仅包含 VARCHAR(217) 的表进行试验,我能够创建 100 列。下一列(第 101 列)的最大大小是 VARCHAR(78)。
CREATE TABLE IF NOT EXISTS test
(
col1 VARCHAR(14) NOT NULL,
col2 VARCHAR(14) NOT NULL,
...
col100 VARCHAR(217) NOT NULL,
col101 VARCHAR(78) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
我发现原因如下:
- 2 个字节用于存储长度值且 NOT NULL
- 217x3 = 651 字节用于页外存储
- 所以 100 列 = 2x100 + 651x100 = 65300
- 最大值为 65535 - 65300 = 235 个字节
- 因此,我们可以再容纳 1 列的最大值 (235 - 2) / 3 = 77.666666666666667 … 或 VARCHAR(78)
将上述内容应用于 VARCHAR(128),我预计 65535 / (128x3+2) 或 169 列,最后一列可以适合 VARCHAR(99)。这确实是真的。这有效:
CREATE TABLE IF NOT EXISTS test
(
col1 VARCHAR(128) NOT NULL,
col2 VARCHAR(128) NOT NULL,
...
col169 VARCHAR(128) NOT NULL,
col170 VARCHAR(99) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
但是,对 VARCHAR(64) 应用相同的逻辑是行不通的。应用上面相同的逻辑,我希望有 337 列。但是,我只能得到 197 列。除此之外,我得到 Row size too large (> 8126) 错误。
CREATE TABLE IF NOT EXISTS test
(
col1 VARCHAR(64) NOT NULL,
col2 VARCHAR(64) NOT NULL,
...
col196 VARCHAR(64) NOT NULL,
col197 VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
对于 VARCHAR(14) 也是如此。在遇到 Row size too large (> 8126) 错误之前,我还可以创建最多 197 列。我进一步体验并发现直到 VARCHAR(109),我会得到 Row size too large (> 8126) 错误。但是对于 VARCHAR(110),我会得到一个不同的错误使用的表类型的最大行大小,不包括 BLOB,是 65535。。
MySQL documentation 说
当使用 ROW_FORMAT=DYNAMIC 创建表时,InnoDB 可以完全离页存储长可变长度列值(对于 VARCHAR、VARBINARY 以及 BLOB 和 TEXT 类型),聚集索引记录仅包含 20 字节指向溢出页面的指针。
我怀疑这是这里发生的事情,InnoDB 会将一些值完全存储在页面之外,因为 64 长度的 utf8 字符串中的 197 个不可能适合 8126。如果所有 64 长度的 utf8 字符串都存储在行内,我预计最多 41 列( 8126 / (64x3+2) )。
任何帮助表示赞赏。我正在使用 MySQL 5.7.26-29,梭子鱼文件格式和 16K 页面大小。
【问题讨论】:
-
如果你有 1000 列的表,我建议你从基本的关系数据库设计课程开始
-
不幸的是,考虑到这个特定的用例,规范化并不可行。这就是为什么它必须以扁平的方式完成,这迫使我发现这一点。
-
公式以不同的方式混乱。