数据库设计原则
目录
在满足需求的前提下,尽量选择占用空间小的数据类型
好处
一、数值类型
1. 整数类型
2. 小数/浮点数类型
二、字符串类型
三、日期与时间类型
四、其他类型
在满足需求的前提下,尽量选择占用空间小的数据类型
好处
- 减少磁盘 I/O:数据页能容纳更多行,查询时一次性读入内存的有效数据更多,速度更快。
- 减少内存占用:数据库缓冲池可以缓存更多的数据页,提高缓存命中率。
- 提升网络传输效率:特别是在分布式数据库或客户端/服务器架构中,返回结果集的数据量更小。
一、数值类型
1. 整数类型
类型 | 字节 | 有符号范围 | 无符号范围 | 适用场景 |
---|---|---|---|---|
TINYINT | 1 | -128 ~ 127 | 0 ~ 255 | 状态标志(如:0-禁用,1-启用)、年龄、小范围分类ID |
SMALLINT | 2 | -32,768 ~ 32,767 | 0 ~ 65,535 | 年份、中期分类ID、城市ID、端口号 |
MEDIUMINT | 3 | -8,388,608 ~ 8,388,607 | 0 ~ 16,777,215 | 不太常用,可作为INT 和SMALLINT 之间的折中 |
INT | 4 | -2^31 ~ 2^31-1 | 0 ~ 2^32-1 | 最常用的整数类型。用户ID、订单ID、大分类ID、数量等。 |
BIGINT | 8 | -2^63 ~ 2^63-1 | 0 ~ 2^64-1 | 自增主键(当数据量极其庞大时)、雪花算法ID、金融领域的大额金额(以分为单位) |
选择建议:
-
优先选择无符号
UNSIGNED
:如果你的数据确定不会是负数(如ID、年龄、数量),一定要加上UNSIGNED
,这能让你在同样的字节数下使用更大的正数范围。 -
能用
TINYINT
就别用INT
:比如性别
字段,用0
和1
表示就足够了,用INT
会浪费3个字节。
2. 小数/浮点数类型
类型 | 字节 | 特点 | 适用场景 |
---|---|---|---|
FLOAT | 4 | 单精度浮点数,近似计算 | 对精度要求不高的科学计算、传感器数据 |
DOUBLE | 8 | 双精度浮点数,近似计算 | 需要更大范围或精度的科学计算 |
DECIMAL(M, D) | 变长 | 精确小数,M是总位数,D是小数位 | 金融、货币相关(如:金额DECIMAL(10,2) )、需要精确计算的数值 |
选择建议:
-
精确计算用
DECIMAL
:凡是和钱有关的,必须用DECIMAL
,避免浮点数精度丢失。 -
非精确计算用
FLOAT/DOUBLE
:比如存储一个产品的平均评分、经纬度等,这些数据本身就不绝对精确,可以使用浮点数以节省空间。
二、字符串类型
类型 | 最大长度 | 特点 | 适用场景 |
---|---|---|---|
CHAR(N) | 255字符 | 定长。不足长度会用空格填充。存取效率高。 | 长度固定或几乎固定的短字符串。例如:MD5 哈希值(32位)、UUID (36位)、国家代码(如'CN','US')、枚举代码(如‘M’, ‘F’)。 |
VARCHAR(N) | 65,535字节 | 变长。只用空间存储实际内容,前面有1-2个字节记录长度。 | 绝大多数字符串场景。例如:用户名、邮箱、地址、标题、描述。关键是合理设置N,不要盲目给很大值。 |
TINYTEXT | 255字节 | 变长 | 短文本、缓存内容 |
TEXT | 64KB | 变长 | 文章正文、帖子内容、商品详情 |
MEDIUMTEXT | 16MB | 变长 | 更大的文本,如书籍内容、代码日志 |
LONGTEXT | 4GB | 变长 | 极大的文本 |
选择建议:
- 定长与变长:CHAR和VARCHAR的选择是经典问题。如果字段长度变化不大(比如都是2个字符或都是32个字符),用CHAR(N)效率更高。如果长度变化很大(比如用户名从3字符到20字符),用VARCHAR(N)更省空间。
- 不要过度使用 TEXT:TEXT类型的设计初衷是存储大段文本。对于像“用户昵称”、“商品标题”这种长度有限的字段,应该优先使用VARCHAR并设置一个合理的上限(如VARCHAR(100))。因为TEXT类型有额外的开销,且通常不能有默认值。
- 为 VARCHAR 设置合理的长度:VARCHAR(100)和VARCHAR(500)在存储"Hello"时占用的空间是一样的。但为什么不全设为500?因为:
- 内存临时表:MySQL在内存中创建临时表时会使用定义的长度,过大的N会浪费内存。
- 前端和后端校验也会依赖这个长度。
三、日期与时间类型
类型 | 字节 | 格式 | 范围 | 适用场景 |
---|---|---|---|---|
DATE | 3 | ‘YYYY-MM-DD’ | ‘1000-01-01’ ~ ‘9999-12-31’ | 仅需要日期,如生日、入职日期、创建日期(如果不需要时间) |
TIME | 3 | ‘HH:MM:SS’ | ‘-838:59:59’ ~ ‘838:59:59’ | 仅需要时间,如会议时间、定时任务时间 |
DATETIME | 8 | ‘YYYY-MM-DD HH:MM:SS’ | ‘1000-01-01 00:00:00’ ~ ‘9999-12-31 23:59:59’ | 需要完整的日期和时间,如订单创建时间、日志记录时间。与时区无关。 |
TIMESTAMP | 4 | ‘YYYY-MM-DD HH:MM:SS’ | ‘1970-01-01 00:00:01’ UTC ~ ‘2038-01-19 03:14:07’ UTC | 记录数据行的最后修改时间、需要自动更新时间的字段、需要时区转换的场景。与时区有关。 |
YEAR | 1 | ‘YYYY’ | 1901 ~ 2155 | 只需要存储年份,如毕业年份、产品发布年份 |
选择建议:
- TIMESTAMP vs DATETIME:
- 空间:TIMESTAMP只要4字节,是DATETIME的一半,优先考虑。
- 功能:TIMESTAMP会自动转换为UTC时间存储,并在检索时转换回当前时区。可以设置ON UPDATE CURRENT_TIMESTAMP自动更新。
- 范围:TIMESTAMP有2038年问题,如果需要存储更久远的时间,必须用DATETIME。
- 只存日期就用 DATE:比如birthday字段,用DATE比用DATETIME或TIMESTAMP节省很多空间。
四、其他类型
-
ENUM:枚举类型。在内部用整数存储,但显示为字符串。非常适合状态、类型等固定类别的字段。
-
优点:非常紧凑。
-
缺点:增加新的枚举值需要修改表结构。
-
场景:
status ENUM('pending', 'active', 'inactive')
。
-
-
SET:集合类型。可以存储多个枚举值。
-
场景:用户标签、权限位。
-
-
BIT(M):位字段类型。M是位数(1-64)。
-
场景:存储真正的位标志,如一个字节(BIT(8))存储8个布尔开关。
-
-
BLOB:二进制大对象,用于存储图片、文件等二进制数据。通常不建议直接存在数据库,而是存文件路径在数据库中,文件本身存在对象存储或文件系统中。
场景 | 糟糕的选择 | 推荐的选择 | 理由 |
---|---|---|---|
用户状态 | VARCHAR(10) | TINYINT 或 ENUM('active', 'inactive') | 数值/枚举比字符串更高效 |
文章标题 | TEXT | VARCHAR(200) | 合理设置长度,避免TEXT开销 |
金额 | FLOAT/DOUBLE | DECIMAL(10, 2) | 保证精度,避免计算误差 |
创建时间 | VARCHAR(20) | TIMESTAMP 或 DATETIME | 专用类型,支持日期计算和排序 |
性别 | VARCHAR(1) | ENUM('M', 'F') 或 TINYINT(1) | 空间更小,语义更清晰 |
用户ID(预计千万级) | BIGINT | INT UNSIGNED | INT UNSIGNED 范围达42亿,足够且节省4字节 |