背景
记得还在银行做开发,投产上线时,项目发版前,要提DDL的sql工单,mysql加1个字段,因为这张表为下游数据入湖入仓用的,长度较大。在测试库加字段没问题,但生产库字段加不上。
先说结论
投产前,开发同学测试库和生产库,没对比出字段长度不一致,在 MySQL 中,每行的最大大小限制通常是 65535 字节(即 64KB 减去一些开销),当新增字段时,超了这个阈值,导致新字段加不上。
前置知识
在 MySQL 中,每行的最大大小限制通常是 65535 字节(即 64KB 减去一些开销),这个限制适用于所有列的总和,不仅仅是 VARCHAR 列。当你说“每行 VARCHAR 阈值最小是 65535”时,这个表述可能有些误导。实际上,65535 字节是整行的最大大小限制,而不是 VARCHAR 列的最小或最大阈值。
VARCHAR 列本身有一个最大长度限制,这个限制取决于 MySQL 的版本和配置。在大多数情况下,单个 VARCHAR 列的最大长度可以达到 65535 字节,但这实际上是不现实的,因为还需要考虑行中的其他列以及存储引擎所需的元数据。实际上,单个 VARCHAR 列的长度很少会接近这个数值,因为还需要考虑其他列和行的总大小限制。
更常见的情况是,你会遇到由于整行大小超过限制而导致的错误。当定义表时,你需要确保所有列的总和(包括 VARCHAR、TEXT、BLOB 和其他类型)不会接近或超过这个限制。如果接近或超过限制,你可能需要调整列的数据类型、大小或使用其他策略来减少行的大小。
请注意,TEXT 和 BLOB 类型的列在存储时不会计入每行的 65535 字节限制,因为它们的值存储在表的外部,只在表中保留一个指针。因此,如果你需要存储大量数据,考虑使用这些类型而不是增加 VARCHAR 列的大小。
总结起来,每行的最大大小限制是 65535 字节,而不是 VARCHAR 列的最小或最大阈值。在设计表结构时,需要确保所有列的总大小不会接近或超过这个限制。
场景复现
本地环境复现
mysql版本8.0.33
数据库字符集utf8mb4
比如有张t_user表,表结构如下:
CREATE TABLE `t_user` (`name` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '姓名',`address` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '住址',`dept` varchar(10000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '部门',`position` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '职务',`work` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '工作'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
现打算加个salary字段
执行如下sql
ALTER TABLE t_user
ADD salary VARCHAR(500);
发现新字段添加失败,报错:1118 - 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
分析
在 utf8mb4 字符集情况下(这是为了支持所有的 Unicode 字符,包括表情符号),1个字符可能需要最多 4 个字节。因此,在 utf8mb4 字符集中,VARCHAR(10) 字段可能最多占用 40 个字节。
上述t_user表单行数字节总数=(1000+2000+10000+1000+2000+500)*4=66000 > 65535最大阈值, 所以salary字段自然加不上。
警示
当设计表和新增字段时,一定用合理的字段长度,注意单行最大字节阈值65535,生产提交DDL工单加字段前,比较好测试库和生产库是否一致。