写数据库表结构这件事,看起来是所有开发的基本功,但很多线上事故、性能瓶颈、改表痛苦,其实在最初建表那一刻就已经埋下了。我见过太多人随手CREATE TABLE一把梭,字段类型不讲究,约束乱加,等业务跑起来才发现索引建错、数据类型扛不住、删字段要锁表一小时。这篇文章我打算把建表阶段值得注意的细节系统梳理一遍,整理成18个可以落地的小技巧,覆盖字段类型、约束设计、扩展性、工程规范几个层面。不扯理论,全是实践中验证过的经验。
1. 先把字段类型和长度定明白——建表最容易忽略的地基
很多人建表时对字段类型的理解停留在"能存就行"的层面,但实际上类型选错,轻则浪费存储空间,重则让索引失效、计算出错、后期迁移要命。这一节先捋一遍最基础也最关键的几个选择。
1.1 技巧1:整数类型选型,别为了省空间委屈自己
整数几乎是每张表都有的字段类型,常见的有TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT。区别在于占用字节数和取值范围,我用一张表把对应关系列清楚:
| 类型 | 字节数 | 有符号范围 | 常见用途 |
|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 状态码、开关值 |
| SMALLINT | 2 | -32768 ~ 32767 | 枚举值、小范围计数 |
| MEDIUMINT | 3 | -8388608 ~ 8388607 | 中等计数 |
| INT | 4 | -2147483648 ~ 2147483647 | 常规业务ID |
| BIGINT | 8 | -9223372036854775808 ~ 9223372036854775807 | 雪花ID、大表主键 |
一个很常见的坑:默认用INT做主键,时间一长数据量涨到几十亿,INT上限到不了,只能改表,而改主键类型在MySQL里是要重建表的,几千万行数据直接锁表几小时。我建议主键字段直接上BIGINT,尤其在分库分表、使用分布式ID方案的场景下,这几乎是必须的。反过来,如果字段只是存性别、状态码、删除标记这类个位数取值的,用TINYINT就够了,不要每个字段都上INT,一张表几十个字段,累积下来空间差异非常明显。
另外注意MySQL 8.0.17开始已经废弃了INT(11)这种显示宽度的写法,以后不要再用INT(11)去限制显示长度了,它不影响存储范围,只影响显示补零,现在写出来反而会触发警告。
1.2 技巧2:金额字段用decimal,别碰float/double
涉及金额、价格、费率、积分这类需要精确计算的字段,一律使用DECIMAL,严禁使用FLOAT或DOUBLE。原因很简单:FLOAT和DOUBLE是浮点数,存储的是近似值,在加减乘除时会出现精度丢失。举个最直观的例子,0.1 + 0.2用浮点数算出来不是0.3,而是0.30000000000000004。金额算错一分钱,财务对不上账,这种事故我见过不止一次。
DECIMAL是定点数,按十进制存储,能够精确表示小数。定义时有两个参数要确定:总位数和小数位数。比如DECIMAL(10,2)表示总长度10位,其中小数2位,整数部分最多8位。对于绝大多数业务,金额用DECIMAL(10,2)够用,如果涉及汇率、利率这种精度要求更高的场景,考虑DECIMAL(18,4)或更长的位数。
还有一点容易被忽略:不同数据库的DECIMAL实现有细微差别,但MYSQL、Oracle、PostgreSQL都支持标准用法。不要试图用BIGINT存"分"来规避精度问题,虽然可行,但查询时到处除以100,代码可读性和维护成本都吃亏。
1.3 技巧3:varchar长度别随手写255
字符串字段的长度规划,是个很低调但影响面很大的细节。先说一个大原则:长度按业务真实约束来定,不要每个字符串字段都写VARCHAR(255)。
原因有两个层面。第一层是存储空间:VARCHAR是变长字符串,虽然超长部分不会直接占满,但当你给它定义255时,MySQL内部要按最大长度去做排序和临时表操作时的内存分配。第二层是索引:在InnoDB中,单列索引长度有限制,早期版本是767字节,新版本是3072字节。在utf8mb4字符集下,一个字符最多占4字节,如果字段定义过长,比如VARCHAR(255),255×4=1020字节,单列索引能撑住,但如果你想在多个大字段上建联合索引,就很容易超出索引长度限制,只能被迫缩短字段或者用前缀索引。
实操建议:手机号用VARCHAR(20),邮箱用VARCHAR(100),用户名根据产品逻辑定,一般VARCHAR(50)就够,地址、备注这类内容可能较长的,用VARCHAR(255)或直接上TEXT,URL用VARCHAR(2048)。核心原则是:先想清楚这个字段真实的最大长度,再往上留20%到50%余量即可。
1.4 技巧4:时间字段别用varchar存,按精度选类型
很多新手会把时间戳直接存成字符串,比如"2024-03-15 14:30:00",甚至存Unix时间戳的字符串形式。这种做法非常不推荐,原因很直接:无法用数据库自带的时间函数做区间统计、排序、格式化,索引效率也比原生时间类型差,还容易因格式不统一产生脏数据。比较不同格式的字符串,比如"2024-3-5"和"2024-03-05"排序结果会是乱的。
时间字段的选择,主要看精度需求:
- 只需要日期,用
DATE,占用3字节,格式是2024-03-15。 - 需要精确到秒,用
DATETIME或TIMESTAMP,占用5到8字节(My
