CHAR始终按声明长度固定分配空间且不溢出,VARCHAR按实际长度存储并支持溢出页;CHAR(1)与VARCHAR(1)在单字节字符集下物理占用相近,但utf8mb4下CHAR仍固定、VARCHAR可变;CHAR受PAD_CHAR_TO_FULL_LENGTH影响读取行为,VARCHAR不受影响。
CHAR和VARCHAR的存储长度固定性差异在哪
CHAR字段在InnoDB中**始终按声明长度分配空间**,哪怕只存1个字符。比如
存
,实际仍占32个字符位置(utf8mb4下最多128字节),尾部用空格填充;而
只存
时,仅用1字节数据 + 1字节长度标识 = 2字节(长度≤255时)。
关键不是“有没有空格”,而是InnoDB行格式是否允许压缩或溢出——
因长度固定,无法触发行溢出;
则可能触发:
当单个
字段内容长度 > 页内剩余空间(通常约8KB)时,InnoDB会把超长部分移入溢出页,行内只留20字节指针
永远不溢出,哪怕
(虽然语法上只允许≤255,但逻辑上它“拒绝变长”)
即使
声明长度很大(如
),只要实际内容短,就不会溢出
溢出页只对VARCHAR生效,CHAR完全不参与
InnoDB的溢出页(off-page storage)机制只针对可变长度列,且前提是该列实际数据长度超过阈值(约768字节)。
类型被设计为“不可溢出”:它的长度在建表时就固化,引擎不会为它生成20字节指针,也不会拆分存储。
这意味着:
如果误用
存短文本(如UUID),会浪费大量空间,且无法利用溢出优化
存同样UUID,实际只占37字节(32字符+5字节长度标识?不,utf8mb4下32字符最多128字节,长度标识仍为1字节),更省空间也更灵活
一旦某
列实际写入超长内容(比如一段base64图片),InnoDB自动启用溢出页,避免单行撑爆16KB页限制
为什么CHAR(1)和VARCHAR(1)在存储上几乎没差别
当声明长度极小(如1–255)且字符集为单字节(
)时,
和
物理占用都是1字节——前者无填充开销(因为刚好满),后者无长度标识冗余(1字节长度标识+1字节数据 = 2字节,但
也是1字节+隐式长度信息?不,
不存长度标识)。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
但真实场景中这个“几乎没差别”很脆弱:
换到
后,
可能占1–4字节(取决于字符),但依然固定;
则按实际字符字节数 + 1字节标识
无法存空字符串
(会被转成单个空格),而
可以
如果字段要索引,
索引效率略高(定长跳转快),但差距微乎其微,不构成选型依据
别忽略PAD_CHAR_TO_FULL_LENGTH对CHAR的干扰
默认情况下MySQL检索
会删掉尾部空格,看起来和
一样;但一旦开启
模式(比如某些ORM或BI工具强制设置),
返回值就会带满填充空格,导致字符串比较、JSON序列化、前端渲染出问题。
这种模式切换不改变存储,只改读取行为,却常被忽略:
应用层代码假设
返回
,但开了该模式后实际是
(7个空格)
完全不受此SQL mode影响,行为稳定
线上环境排查时,容易误判为数据写入异常,其实是读取侧SQL mode漂移
CHAR(32)'a'VARCHAR(32)'a'CHARVARCHARVARCHARCHARCHAR(1000)VARCHARVARCHAR(65535)CHARCHAR(255)VARCHAR(255)VARCHARlatin1CHAR(1)VARCHAR(1)CHAR(1)CHARutf8mb4CHAR(1)VARCHAR(1)CHAR(1)''VARCHAR(1)CHAR(1)CHARVARCHARPAD_CHAR_TO_FULL_LENGTHCHARCHAR(10)'abc''abc 'VARCHAR