VARCHAR(n)按实际长度行内存储,TEXT则溢出至外部页并仅存前缀和指针;VARCHAR适合短且需索引的字段,TEXT仅用于超长内容或明确无需B+树索引的场景。
两者在磁盘上都按实际长度存储,但 VARCHAR 存在行内存储上限(InnoDB 默认 65535 字节总行宽),而 TEXT 类型的数据默认被移出主记录页

这意味着:
VARCHAR(1000) 存 10 个字符,只占约 11 字节(1 字节长度 + 内容);存满则占 1001 字节TEXT 存 10 个字符,仍需额外访问外部页(哪怕很小),带来一次潜在的随机 I/OVARCHAR 定义过长(如 VARCHAR(65535)),即使内容很短,也可能因行宽超限触发隐式转为 TEXT 或报错选 VARCHAR 的典型场景:字段长度可预期、常参与 WHERE/ORDER BY/GROUP BY、需要索引全文或前缀(如 INDEX(col(191)))。
选 TEXT 的刚性原因只有两个:
VARCHAR 最大理论值即此,且受字符集影响,utf8mb4 下实际可用约 16383 个字符)TEXT 不支持无前缀索引,CREATE INDEX ON t(c) 会报错,必须写 CREATE INDEX ON t(c(255)))注意:TINYTEXT/MEDIUMTEXT/LONGTEXT 仅影响最大长度和内部存储策略,不改变“溢出存储”这一核心行为。
以下行为在 TEXT 上代价更高:
ORDER BY text_col 且无法走索引,MySQL 常将整列加载进 sort buffer 或磁盘临时表,而 VARCHAR 更可能被截断或优化掉TEXT 字段默认以完整值记录(STATEMENT 模式下尤其危险),大字段更新易拖慢从库;VARCHAR 因长度可控,影响小得多TEXT 外部页的缓存粒度是 16KB 整页,哪怕只读取一个 100 字节的 TEXT 值,也可能带入整个页;VARCHAR 则随行一起加载,更紧凑看似只是类型名不同,但实际会影响很多周边行为:
VARCHAR 可直接传给 JSON_EXTRACT(),但若字段定义为 TEXT 且含非 UTF8 字符,可能触发隐式转换失败TEXT / BLOB 类型不能作为分区键,VARCHAR 可以ALTER TABLE ... MODIFY COLUMN c TEXT 在 MySQL 5.7+ 通常是 instant,但若原字段是 VARCHAR(2000) 且已存大量数据,切换时仍可能触发表拷贝(因外部存储结构变更)真正决定性能的往往不是单次查询,而是这些“不起眼”的交互点——比如一个本该走索引的 WHERE text_col = ?,因为没加前缀索引,最终变*表扫描加逐行读外部页。