Skip to main content

7.表列数和行大小的限制

列数限制

MySQL 对每个表有 4096 列的硬限制,但对于给定的表,有效最大值可能会更少。 确切的列限制取决于几个因素:

  • 表的最大行大小限制了列的数量(以及可能的大小),因为所有列的总长度不能超过此大小。
  • 各个列的存储要求限制了适合给定最大行大小的列数。 某些数据类型的存储要求取决于存储引擎、存储格式和字符集等因素。
  • 存储引擎可能会施加额外的限制来限制表列数。 例如,InnoDB 每个表的列数限制为 1017。
  • 功能键部分被实现为隐藏的虚拟生成存储列,因此表索引中的每个功能键部分都计入表总列限制。

行大小限制

给定表的最大行大小由几个因素决定:

  • 即使存储引擎能够支持更大的行,MySQL 表的内部表示也具有 65,535 字节的最大行大小限制。 BLOB 和 TEXT 列仅对行大小限制贡献 9 到 12 个字节,因为它们的内容与行的其余部分分开存储。
  • InnoDB 表的最大行大小适用于本地存储在数据库页内的数据,对于 4KB、8KB、16KB 和 32KB innodb_page_size 设置,略小于半页。 例如,对于默认的 16KB InnoDB 页大小,最大行大小略小于 8KB。 对于 64KB 页面,最大行大小略小于 16KB。
  • 如果包含可变长度列的行超过了 InnoDB 最大行大小,InnoDB 会选择可变长度列进行外部页外存储,直到该行符合 InnoDB 行大小限制。 对于存储在页外的可变长度列,本地存储的数据量因行格式而异。 有关更多信息,请参见第 17.10 节 “InnoDB 行格式”。
  • 不同的存储格式使用不同数量的页眉和页尾数据,这会影响行的可用存储量。

行大小限制示例

以下 InnoDB 和 MyISAM 示例演示了 MySQL 最大行大小限制 65,535 字节。 无论存储引擎如何,都会强制执行该限制,即使存储引擎可能能够支持更大的行。

mysql> CREATE TABLE t (a VARCHAR(10000), b VARCHAR(10000),
c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),
f VARCHAR(10000), g VARCHAR(6000)) ENGINE=InnoDB CHARACTER SET latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used
table type, not counting BLOBs, is 65535. This includes storage overhead,
check the manual. You have to change some columns to TEXT or BLOBs
mysql> CREATE TABLE t (a VARCHAR(10000), b VARCHAR(10000),
c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),
f VARCHAR(10000), g VARCHAR(6000)) ENGINE=MyISAM CHARACTER SET latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used
table type, not counting BLOBs, is 65535. This includes storage overhead,
check the manual. You have to change some columns to TEXT or BLOBs

在下面的 MyISAM 示例中,将列更改为 TEXT 可避免 65,535 字节的行大小限制并允许操作成功,因为 BLOB 和 TEXT 列仅对行大小贡献 9 到 12 个字节。

mysql> CREATE TABLE t (a VARCHAR(10000), b VARCHAR(10000),
c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),
f VARCHAR(10000), g TEXT(6000)) ENGINE=MyISAM CHARACTER SET latin1;
Query OK, 0 rows affected (0.02 sec)

对于 InnoDB 表,该操作会成功,因为将列更改为 TEXT 可以避免 MySQL 65,535 字节的行大小限制,并且 InnoDB 可变长度列的页外存储可以避免 InnoDB 行大小限制。

mysql> CREATE TABLE t (a VARCHAR(10000), b VARCHAR(10000),
c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),
f VARCHAR(10000), g TEXT(6000)) ENGINE=InnoDB CHARACTER SET latin1;
Query OK, 0 rows affected (0.02 sec)

可变长度列的存储包括长度字节,这些字节计入行大小。 例如,VARCHAR(255) CHARACTER SET utf8mb3 列需要两个字节来存储值的长度,因此每个值最多可以占用 767 个字节。

创建表 t1 的语句成功,因为列需要 32,765 + 2 字节和 32,766 + 2 字节,这在最大行大小 65,535 字节之内:

mysql> CREATE TABLE t1
(c1 VARCHAR(32765) NOT NULL, c2 VARCHAR(32766) NOT NULL)
ENGINE = InnoDB CHARACTER SET latin1;
Query OK, 0 rows affected (0.02 sec)

创建表 t2 的语句失败,因为虽然列长度在最大长度 65,535 字节之内,但需要两个额外的字节来记录长度,这导致行大小超过 65,535 字节:

mysql> CREATE TABLE t2
(c1 VARCHAR(65535) NOT NULL)
ENGINE = InnoDB CHARACTER SET latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used
table type, not counting BLOBs, is 65535. This includes storage overhead,
check the manual. You have to change some columns to TEXT or BLOBs

将列长度减少到 65,533 或更小可以使语句成功。

mysql> CREATE TABLE t2
(c1 VARCHAR(65533) NOT NULL)
ENGINE = InnoDB CHARACTER SET latin1;
Query OK, 0 rows affected (0.01 sec)

对于MyISAM表,NULL列需要行中额外的空间来记录它们的值是否为NULL。 每个 NULL 列需要额外一位,四舍五入到最接近的字节。

创建表 t3 的语句失败,因为 MyISAM 除了可变长度列长度字节所需的空间外,还需要 NULL 列的空间,导致行大小超过 65,535 字节:

mysql> CREATE TABLE t3
(c1 VARCHAR(32765) NULL, c2 VARCHAR(32766) NULL)
ENGINE = MyISAM CHARACTER SET latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used
table type, not counting BLOBs, is 65535. This includes storage overhead,
check the manual. You have to change some columns to TEXT or BLOBs

对于 4KB、8KB、16KB 和 32KB innodb_page_size 设置,InnoDB 将行大小(对于本地存储在数据库页面内的数据)限制为略小于数据库页面的一半,对于 64KB 页面,限制为略小于 16KB。

创建表 t4 的语句失败,因为定义的列超出了 16KB InnoDB 页的行大小限制。