行格式 | 紧凑的存储特性 | 增强的可变长度列存储 | 大型索引键前缀支持 | 压缩支持 | 支持的表空间类型 |
---|---|---|---|---|---|
REDUNDANT | N | N | N | N | system, file-per-table, general |
COMPACT | Y | N | N | N | system, file-per-table, general |
DYNAMIC | Y | Y | N | N | system, file-per-table, general |
COMPRESSED | Y | Y | Y | Y | file-per-table, general |
开发中常用的是DYNAMIC行格式,这里着重对它进行介绍,其他格式说明请查阅官网。
行DYNAMIC格式提供与行格式相同的存储特性COMPACT,但增加了对长可变长度列的增强存储功能,并支持大索引键前缀
https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html
DECIMAL(和 NUMERIC)列的值使用二进制格式表示,该格式将九个十进制(基数 10)数字打包成四个字节。 每个值的整数和小数部分的分开存储。 九个十进制数字的每个倍数需要四个字节,“剩余”数字需要四个字节的一部分。 下表给出了多余数字所需的存储空间:
MySQL5.6.4 之后创建的表允许TIME、DATETIME 和 TIMESTAMP这些类型具有小数部分,这额外需要 0 到 3 个字节,具体取决于存储值的小数秒精度:
M:非二进制字符串类型的声明列长度(以字符为单位)和二进制字符串类型的字节数;
N:给定字符串值的实际长度(以字节为单位);
https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html
MySQL 规定每个表只能有 4096 列,但对于给定的表,有效最大值可能会更少。 确切的列限制取决于几个因素:
表的最大行大小由几个因素决定:
1)在以下 InnoDB 和 MyISAM 示例中演示了 65,535 字节的 MySQL 最大行大小限制。 无论存储引擎如何,都会强制执行该限制,即使存储引擎可能能够支持更大的行:
-- InnoDB引擎
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
-- MyISAM 引擎
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
列更改为 TEXT 可避免 65535 字节的行大小限制,因为 BLOB 和 TEXT 列仅占 9 到 12 字节,并且 InnoDB 变长列的页外存储避免了 InnoDB 行大小限制:
-- MyISAM中TEXT的使用
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> 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)
2) 可变长度列的存储包括计入行大小的长度字节。 例如,VARCHAR(255) CHARACTER SET utf8mb3 列需要两个字节来存储值的长度,因此每个值最多可以占用 767 个字节:
latin1+InnaDB:
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)
使用的是Latin1,一个字符一个字节,因此列需要 32765+2 + 32766+2 字节 < 65535;
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
65535 + 2 > 65535,因此创建失败;
mysql> CREATE TABLE t2
(c1 VARCHAR(65533) NOT NULL)
ENGINE = InnoDB CHARACTER SET latin1;
Query OK, 0 rows affected (0.01 sec)
65533 + 2 = 65535,创建成功
3)对于 MyISAM 表,NULL 列需要在行中额外的空间来记录它们的值是否为 NULL。 每个 NULL 列多占用一位,四舍五入到最接近的字节
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
32765 + 2 + 32766 + 2 = 65535,但是对于MyISAM还需要额外的空间来存储null值,因此创建失败
4)对于 4KB、8KB、16KB 和 32KB innodb_page_size 设置,InnoDB 将行大小(对于本地存储在数据库页面中的数据)限制为略小于数据库页面的一半,而对于 64KB 页面限制为略小于 16KB
tip: 对于64KB的一半为什么是16,目前也没在官方文档找到较为详细的解释,只有下面的解释:
mysql> CREATE TABLE t4 (
c1 CHAR(255),c2 CHAR(255),c3 CHAR(255),
c4 CHAR(255),c5 CHAR(255),c6 CHAR(255),
c7 CHAR(255),c8 CHAR(255),c9 CHAR(255),
c10 CHAR(255),c11 CHAR(255),c12 CHAR(255),
c13 CHAR(255),c14 CHAR(255),c15 CHAR(255),
c16 CHAR(255),c17 CHAR(255),c18 CHAR(255),
c19 CHAR(255),c20 CHAR(255),c21 CHAR(255),
c22 CHAR(255),c23 CHAR(255),c24 CHAR(255),
c25 CHAR(255),c26 CHAR(255),c27 CHAR(255),
c28 CHAR(255),c29 CHAR(255),c30 CHAR(255),
c31 CHAR(255),c32 CHAR(255),c33 CHAR(255)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARSET latin1;
ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB may help.
In current row format, BLOB prefix of 0 bytes is stored inline.
以上例子使用InnoDB的默认页大小16KB,因此最大行大小会限制在8KB以下,即 8 * 1024 = 8192,由于是略小于,从报错信息中也可以发现,真正的行大小是8126
而 33 * 255 = 8415 > 8126,因此创建失败。
这块内容是我在MySQL官网上翻译总结而来,内容比较枯燥,并且实际开发中也并不会注意这么多,因为大多数业务表并不会离谱道到超出字段数量及行大小限制。不过在认真阅读的时候也确实对MySQL有了更深的认识,在以后创建表的时候也会有意识无意识的想到相关的规约,这对于之后的开发或纠错或多或少都会有帮助,等真正出现该类问题时至少不会手足无措。如果你不想看官网的全英文文档,就简单看看这里的介绍,当然,更建议仔细阅读官网,其内容会更加详尽。