本文目录导读:
这是一个非常专业且重要的问题,字段类型的选型直接决定了数据库的存储空间、查询性能、写入速度以及数据完整性。
优化选型没有银弹,核心原则是:满足业务需求的前提下,选择最恰当(通常是最小、最严格)的类型。
下面从MySQL(最常见的场景)和通用数据库原则出发,为你拆解优化策略。
核心原则与思考路径
在决定用什么类型之前,可以先问自己5个问题:
- 存什么?(整数?小数?字符串?日期?还是大文本/二进制?)
- 范围有多大?(值的上限和下限是多少?)
- 需要精确计算吗?(涉及金钱必须精确,科学计算可以近似。)
- 需要排序、比较或作为索引吗?(不同类型排序效率差异巨大。)
- 是否允许为空?(尽量设为
NOT NULL,以节省空间并简化逻辑。)
分场景优化建议
整数类型:能用小的,绝不用大的
这是最容易被优化的点,很多人习惯用 INT 或 BIGINT,但可能 MEDIUMINT 或 SMALLINT 就够了。
| 类型 | 存储空间 | 有符号范围 | 无符号范围 | 推荐场景 |
|---|---|---|---|---|
| TINYINT | 1字节 | -128 ~ 127 | 0 ~ 255 | 状态码、性别(0/1/2)、年龄、小枚举值、布尔值 |
| SMALLINT | 2字节 | -32768 ~ 32767 | 0 ~ 65535 | 城市编码、月销量、少量并发数 |
| MEDIUMINT | 3字节 | -838万 ~ 838万 | 0 ~ 1677万 | 中等量级的主键(ID)、统计数字(如文章阅读量) |
| INT | 4字节 | -21亿 ~ 21亿 | 0 ~ 42亿 | 最常用主键、用户ID、订单ID |
| BIGINT | 8字节 | -2^63 ~ 2^63-1 | 0 ~ 2^64-1 | 大数据量主键、雪花ID、极高并发的计数器 |
优化要点:
- 使用
UNSIGNED(无符号):如果你确定值不会是负数(如ID、年龄、数量),加UNSIGNED可以让范围翻倍,相当于用1字节存了2字节的数据。 - 避免使用
INT(11)的括号:INT(11)11只是显示宽度,不影响存储,现代MySQL已不推荐使用。 - 主键选型:大多数场景用
BIGINT UNSIGNED对未来的扩展更安全;小系统用INT UNSIGNED也足够;绝对不要用字符串做主键。
小数与浮点数:精确优先,慎用FLOAT/DOUBLE
- DECIMAL / NUMERIC:强烈推荐用于金钱、金额、汇率。
- 它是定点数,精确存储,不会丢失精度。
- 语法:
DECIMAL(M, D),M是总位数,D是小数位数。DECIMAL(10, 2)存最大99,999,999.99,占用 M+2 字节(实际上INT部分和DEC部分分开编码,更高效)。
- FLOAT / DOUBLE:用于科学计算、统计、近似值(如地理位置经纬度、一些概率)。
- 它们是浮点数,不精确(IEEE 754标准)。
- 绝对不要用FLOAT/DOUBLE存金钱。
- 优化要点:
- 金额用
DECIMAL,如果资金量不大,DECIMAL(10, 2)就够了。 - 如果存百分比(如0.1~1.0),考虑用
DECIMAL(5, 4)或直接用SMALLINT存整数值(如存 1000 代表 10.00%)。
- 金额用
字符串类型:分清定长与变长
最大误区:所有字符串都用 VARCHAR。
| 类型 | 特点 | 存储 | 推荐场景 |
|---|---|---|---|
| CHAR(N) | 定长,长度固定 | N字节(按字符集算,最多255字符) | 固定长度:国家编码(CN)、身份证号(18位)、MD5值(32位)、状态码(Y/N)、性别(M/F) |
| VARCHAR(N) | 变长,实际长度+1~2字节 | 实际字节数 + (1或2)字节开销 | 长度变化大:用户名、邮箱、商品标题、地址、短文本 |
| TINYTEXT / TEXT / LONGTEXT | 大文本,不能有默认值,有专门的行外存储 | 1~4字节前缀 + 实际数据 | 文章正文、日志记录、JSON字符串、评论内容 |
优化要点:
- 能用CHAR不用VARCHAR:对于固定长度,CHAR查询更快(无长度字节,且不产生碎片)。
- VARCHAR长度别滥用:不要一上来就
VARCHAR(255)或VARCHAR(5000)。- MySQL在排序和创建临时表时,会分配
VARCHAR声明的最大长度,越长越耗内存。 VARCHAR(20)和VARCHAR(255)在存一个 “abc” 时,磁盘开销一样(3字节+1),但内存临时表会按255分配,所以长度尽量贴近实际最大值。
- MySQL在排序和创建临时表时,会分配
- TEXT/BLOB的代价:TEXT/BLOB字段行外存储,读取时会导致随机IO,如果能避免,尽量拆到单独表中,或考虑用文件系统存储。
- IP地址:不要用
VARCHAR(15),用INT UNSIGNED(INET_ATON和INET_NTOA函数转换),4字节 vs 15字节。
日期与时间类型:用对原生的,别用字符串
| 类型 | 存储空间 | 范围 | 精确度 | 推荐场景 |
|---|---|---|---|---|
| DATE | 3字节 | 1000-01-01 ~ 9999-12-31 | 日期(无时间) | 生日、入职日期、交易日期 |
| TIME | 3字节 | -838:59:59 ~ 838:59:59 | 时间(或持续时间) | 营业时间、耗时 |
| DATETIME | 5字节 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 时间戳(无时区) | 通用时间存储,适合大多数业务 |
| TIMESTAMP | 4字节 | 1970-01-01 00:00:01 UTC ~ 2038-01-19 | 时间戳(有时区),自动更新 | 创建时间、更新时间(自动设置 ON UPDATE CURRENT_TIMESTAMP) |
| YEAR | 1字节 | 1901 ~ 2155 | 年份 | 毕业年份、年份字段 |
优化要点:
- 永远不要用
VARCHAR存时间:无法做日期函数(如日期加减、区间查询),排序错误,占用空间大。 DATETIMEvsTIMESTAMP:- 需要跨时区应用(全球业务),
TIMESTAMP更合适(自动转UTC)。 - 如果只关心本地时间,或时间可能超过2038年,用
DATETIME。
- 需要跨时区应用(全球业务),
- 时区处理:统一在应用层或数据库连接时设置时区,字段本身建议存UTC时间。
枚举与集合类型:用对枚举,别用字符串替代
| 类型 | 特点 | 推荐场景 |
|---|---|---|
| ENUM | 单选,内部存整数索引(1字节起) | 状态(待支付、已支付、已取消)、性别、部门 |
| SET | 多选,内部存位运算 | 爱好(可多选)、用户权限组合 |
优化要点:
- 好用但需谨慎:ENUM/SET修改枚举列表需要
ALTER TABLE,对生产环境不友好,但如果列表稳定(如性别),它们是性能最优的字符串替代品(内部按整数存储和排序)。 - 替代方案:如果列表经常变,可以换成
TINYINT+ 应用层映射,或者用关联表。
特殊类型
- JSON(MySQL 5.7+):适合存储结构化的、不固定的数据(如用户自定义字段、配置信息),内部自动验证JSON格式,但不适合查询字段内部(除非用虚拟列+索引)。
- 地理空间类型(POINT, LINESTRING, POLYGON):如果业务涉及地图、LBS(基于位置服务),用GIS类型,配套空间索引性能远超普通B+树。
- 布尔值:MySQL没有真正的布尔类型,用
TINYINT(1)或TINYINT UNSIGNED替代,不要用CHAR(1)(Y/N),更不要用VARCHAR。
快速决策流程图
[业务字段]
|
+--> 数字/数值?
| |
| +--> 整数?
| | +--> 状态/性别/小范围? -> TINYINT UNSIGNED
| | +--> 中等ID/统计? -> MEDIUMINT / INT UNSIGNED
| | +--> 大数据ID? -> BIGINT UNSIGNED
| | +--> 主键? -> BIGINT UNSIGNED 或 INT UNSIGNED
| |
| +--> 小数?
| | +--> 金钱/精确值? -> DECIMAL(M, D)
| | +--> 近似值/科学计算? -> FLOAT / DOUBLE
| |
| +--> 布尔? -> TINYINT(1) UNSIGNED
|
+--> 字符串/文本?
| |
| +--> 固定长度/标识? -> CHAR(N) (如MD5、国家码)
| +--> 可变长度/短文本? -> VARCHAR(N) (长度贴近实际)
| +--> 大文本/文章/JSON? -> TEXT / LONGTEXT (谨慎使用)
| +--> IP地址? -> INT UNSIGNED (INET_ATON)
|
+--> 日期/时间?
| |
| +--> 只有日期? -> DATE
| +--> 时间点 + 时区不敏感? -> DATETIME
| +--> 时间点 + 时区敏感/自动更新? -> TIMESTAMP
| +--> 年份? -> YEAR
|
+--> 枚举/固定选项?
+--> 单选? -> ENUM (稳定) 或 TINYINT (灵活)
+--> 多选? -> SET (稳定) 或 VARCHAR(逗号分隔) (不推荐)
最后几点实战建议:
- 优先考虑存储空间:在机械硬盘时代,IO是瓶颈;在SSD时代,IO仍然是瓶颈,尤其对于高并发,少占字节 = 更多缓存命中 = 更快。
- 为未来的扩展留点余地,但不要浪费:主键用
INT还是BIGINT?如果系统百万级用户,INT就够了,如果预期千万到亿级,直接上BIGINT。 - 避免使用
NULL:能设为NOT NULL的尽量设。NULL列需要额外1位标记,索引效率低,查询条件 (IS NULL/IS NOT NULL) 性能差,实在无法避免,用默认值(如0、空字符串)替代。 - 多利用
SHOW TABLE STATUS和EXPLAIN:实际测试修改类型后的磁盘占用和查询性能变化。
好的字段选型,在不增加物理硬件投入的情况下,能带来指数级的性能提升,是DBA和高级开发的基本功。
标签: 类型选型