一句话
选最小的、能装下你数据的类型。 类型小 = 索引小 = 内存命中率高 = 快。
一、整数类型
| 类型 | 字节 | 范围(无符号) | 用途 |
|---|
| TINYINT | 1 | 0 ~ 255 | 状态枚举、布尔 |
| SMALLINT | 2 | 0 ~ 65535 | 较小计数 |
| MEDIUMINT | 3 | 0 ~ 1677w | 较少用 |
| INT | 4 | 0 ~ 42亿 | 大多数主键、ID |
| BIGINT | 8 | 0 ~ 1844亿亿 | 雪花 ID、大表主键 |
1 2 3 4
| gender INT
gender TINYINT UNSIGNED COMMENT '0未知 1男 2女'
|
INT(11) 里的 11 不是长度!只是显示宽度(已废弃),实际仍是 4 字节。
二、字符串类型
| 类型 | 长度 | 适用 |
|---|
| CHAR(n) | 固定 n 字符 | MD5、UUID、固定位手机号 |
| VARCHAR(n) | 可变 ≤ n | 大多数字符串 |
| TEXT (4 种) | 长文本 | 文章正文,不要建普通索引 |
| ENUM | 枚举 | 不推荐,改字段难 |
| JSON | 5.7+ | 半结构化数据 |
CHAR vs VARCHAR
| 维度 | CHAR | VARCHAR |
|---|
| 存储 | 固定,不足补空格 | 1-2 字节长度 + 实际内容 |
| 速度 | 略快(无需算长度) | 略慢 |
| 空间 | 浪费(固定占满) | 节省 |
| 适用 | 长度真正固定 | 长度变化 |
三、日期时间
| 类型 | 字节 | 范围 | 时区 |
|---|
| DATE | 3 | 1000-01-01 ~ 9999-12-31 | 无 |
| TIME | 3 | -838:59:59 ~ 838:59:59 | 无 |
| DATETIME | 8 | 1000 ~ 9999 | 不存时区 |
| TIMESTAMP | 4 | 1970 ~ 2038 | 存 UTC,按 session 时区显示 |
| YEAR | 1 | 1901 ~ 2155 | 无 |
DATETIME vs TIMESTAMP
1 2 3 4 5
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
contract_end DATETIME
|
2038 问题:TIMESTAMP 上限 2038-01-19 03:14:07 UTC,长期合同/出生日期一定用 DATETIME。
四、浮点 vs 定点
| 类型 | 精度 | 用途 |
|---|
| FLOAT (4) / DOUBLE (8) | 不精确 | 科学计算、统计 |
| DECIMAL(M, D) | 精确 | 金额、汇率、价格 |
1 2 3 4 5
| price FLOAT
price DECIMAL(10, 2)
|
五、JSON
1 2 3 4 5 6 7 8 9 10 11 12 13
| profile JSON
INSERT INTO users (profile) VALUES ('{"city": "Taipei", "tags": ["dev"]}');
SELECT profile->>'$.city' FROM users; SELECT * FROM users WHERE JSON_CONTAINS(profile->'$.tags', '"dev"');
ALTER TABLE users ADD city VARCHAR(20) AS (profile->>'$.city') STORED, ADD INDEX idx_city(city);
|
注意:JSON 灵活但失去强 schema,能用普通列就别用 JSON。
六、设计经验
- 能 NOT NULL 就 NOT NULL —— NULL 让索引、统计、计算都更复杂
- 整数无符号
UNSIGNED —— 范围加倍、避免负数业务 bug - 避免 ENUM —— 改值要 ALTER TABLE,用 TINYINT + 字典表
- TEXT/BLOB 单独存表 —— 主表行长可控
- 手机号用 VARCHAR —— +号、前导 0、国家码
- 金额绝不用 FLOAT —— 用 DECIMAL 或存”分”用 INT
速查表
| 想存的内容 | 推荐类型 |
|---|
| 主键 ID | INT UNSIGNED 或 BIGINT |
| 性别/状态 | TINYINT UNSIGNED |
| 用户名 | VARCHAR(64) |
| 邮箱 | VARCHAR(255) |
| 密码 hash | CHAR(60) (bcrypt) |
| 手机号 | VARCHAR(20) |
| 金额 | DECIMAL(10,2) |
| 时间戳 | TIMESTAMP(带时区)/ DATETIME |
| IP 地址 | INT UNSIGNED + INET_ATON(),或 VARBINARY(16) |
| 大文本 | TEXT,单独表 |
| 半结构化 | JSON,慎用 |
参考