高 基础
char与varchar选型#
一句话答案#
char 是定长(不足补空格、存取时去空格),适合长度固定或近似固定的短字段(如手机号、MD5、状态码);varchar 是变长(按实际长度存 + 1~2 字节记长度),适合长度波动大的字段;选型核心是”长度是否固定、是否频繁更新、能否省空间换不产生行迁移”。
核心要点
| 维度 | char(N) | varchar(N) |
|---|---|---|
| 存储 | 固定 N 个字符,不足补空格 | 实际长度 + 1~2 字节长度前缀 |
| 上限 | 最多 255 字符 | 受行大小限制(最大 65535 字节,约几万字符) |
| 取值 | 读取时去掉尾部空格(尾部空格丢失) | 原样存取,保留空格 |
| 适用 | 长度固定/近似固定的短串:手机号、身份证、MD5、UUID、status | 长度波动大的串:用户名、地址、备注、标题 |
| 更新 | 定长,原地更新,不易产生行迁移 | 变长,变长可能导致页内移动甚至行迁移 |
字段类型选型通用原则:
- 够用就好:能用
int不用bigint,能用tinyint表状态不用int,越小的字段索引和缓存效率越高 - 整数优先于字符串:枚举/状态用
tinyint而非字符串;IP 可用int unsigned存 - 金额用
decimal:禁止float/double存钱,浮点有精度误差 - 时间用
datetime或timestamp:timestamp占 4 字节带时区、范围到 2038,datetime占 8 字节范围更大 - 避免
text/blob进主表:大字段拆到扩展表,避免拖慢主表扫描和回表
面试回答(2分钟版)
char 和 varchar 的核心区别是定长还是变长。char(N) 永远占 N 个字符,存不满会用空格补齐,读出来时尾部空格会被去掉;varchar(N) 是变长,实际存多少占多少,再额外加 1 到 2 个字节记录长度(长度小于 255 用 1 字节,否则 2 字节)。所以选型上:长度固定或几乎固定的短字段用 char,比如手机号、身份证、MD5 值、状态码,定长还有个好处是更新时长度不变、原地改不容易产生行迁移;长度波动大的字段用 varchar,比如用户名、地址、备注,能按需省空间。
再往上说字段类型选型的通用原则就是”够用就好、越小越好”:状态枚举用 tinyint 别用字符串,金额一定用 decimal 不能用 float/double 否则有精度问题,时间用 datetime 或 timestamp,大文本 text/blob 尽量拆到扩展表别放主表,否则会拖慢主表扫描。字段越小,单页能放的行越多,索引和 Buffer Pool 的命中率就越高,整体查询更快。
追问与易错
追问方向:
- “char 一定比 varchar 快吗?”→ 不一定。char 定长便于原地更新、对频繁更新的定长字段有利;但 char 会浪费空间(补空格),存储变大反而降低页内行数。短且定长用 char,其余用 varchar
- “varchar(255) 和 varchar(50) 存同样数据占用一样吗?”→ 磁盘存储一样(变长按实际长度),但内存里某些操作(如排序用的临时表、内存表)可能按声明的最大长度分配,所以别无脑开 255
- “为什么金额不能用 float?”→ float/double 是二进制浮点,0.1 这类十进制小数无法精确表示,累加会产生误差,金额必须用 decimal 定点数
- “时间字段 datetime 和 timestamp 怎么选?”→ timestamp 4 字节、带时区转换、范围 1970~2038(有 2038 问题);datetime 8 字节、不带时区、范围更大。需要时区敏感或省空间用 timestamp,否则用 datetime
易错点:
- ❌ 用 char 存变长字段——浪费空间且尾部空格被截断,可能导致数据”被改”
- ❌ 用 float/double 存金额——精度丢失,财务对不上账
- ❌ 字段无脑开 varchar(255) 甚至更大——影响内存临时表和排序效率
- ❌ 状态枚举用字符串存——浪费空间、索引效率低,应用 tinyint + 应用层映射