一线互联网大厂的数据库规范

0 阅读12分钟

前言

从建表到SQL优化,让你少走三年弯路

前几天发表了一篇文章《一线大厂的Git规范》,在全网挺受欢迎的。

今天趁着打铁,跟大家一起聊聊一线互联网大厂的数据库规范,希望的对你会有所帮助。

有些大厂,几百上千人的研发团队,数据库却能保持井井有条。

表结构清晰、命名规范统一、索引设计合理、SQL性能可控。

很多经验还是非常值得借鉴的。

更多项目实战在Java突击队网:susan.net.cn/project

一、为什么大厂如此重视数据库规范?

在聊具体规范之前,我们先理解一个根本问题——为什么大厂把数据库规范看得这么重要?

第一个原因:数据库是“最改不动”的层。

代码可以重构,架构可以演进,但数据库表一旦上线,字段名、字段类型基本就改不动了。

改一个字段名,所有依赖它的业务代码都要改,而且没法预发布测试。每一行DDL都值得你认真写

第二个原因:数据量是“指数级”增长的。

一个“先上线再说”的表,可能三个月就涨到几百万行。等发现问题再来优化,成本是当初规范的十倍甚至百倍。

第三个原因:数据库是整个系统的“命根子”。

代码出问题,最多功能不能用。

数据库出问题,整个系统直接瘫痪。阿里规范里大量使用“强制”条款,正是因为阿里经历过无数双十一的极限考验,深知数据库出问题意味着什么。

说白了,数据库规范不是用来“管人”的,是用来“保命”的。

二、建表规约

从第一行DDL就决定了生死。

2.1 命名规范

它是最容易被忽略、又最难改的部分。

① 表名、字段名必须全小写,禁止大写

阿里规范强制要求:表名、字段名必须使用小写字母或数字,禁止出现数字开头

-- ✅ 正确
CREATE TABLE user_info (...);
CREATE TABLE order_detail (...);

-- ❌ 错误——包含大写字母
CREATE TABLE UserInfo (...);
CREATE TABLE OrderDetail (...);

为什么要全小写?

MySQL在Windows下不区分大小写,但在Linux下默认是区分大小写的。

一旦部署到Linux环境,Systemsystem就是两张不同的表。

用全小写,彻底避免这个坑。

② 表名单数形式,禁止复数

表名表示实体内容,不是实体数量。

-- ✅ 正确
CREATE TABLE user (...);
CREATE TABLE order (...);

-- ❌ 错误——用了复数
CREATE TABLE users (...);
CREATE TABLE orders (...);

③ 禁用保留字

不能使用descrangematchdelayed等MySQL保留字作为表名或字段名。

④ 索引命名统一

索引类型命名格式示例
主键索引pk_字段名pk_id
唯一索引uk_字段名uk_user_name
普通索引idx_字段名idx_create_time

看名字就知道索引类型,排查问题时省一半力气。

⑤ 表名长度不超过32字符

库名、表名、字段名最好不超过32个字符,做到“见名知意”即可。

2.2 字段规范——选对类型,事半功倍

① 布尔字段:is_xxx + unsigned tinyint

这是阿里规范里最经典的条款之一:

-- ✅ 正确
is_deleted TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除:0-未删除,1-已删除'
is_valid TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否有效:0-无效,1-有效'

-- ❌ 错误——字段名不规范
deleted TINYINT COMMENT '是否删除'

1表示是,0表示否。任何字段如果为非负数,必须使用unsigned

特别注意:虽然数据库字段必须叫is_xxx,但对应的Java POJO类的布尔变量不能加is前缀(如不能用isDeleted),需要在resultMap中做映射。否则可能导致序列化失败或RPC框架取值异常。

② 小数类型:一律用decimal,禁止floatdouble

这是金钱相关业务的“铁律”

-- ✅ 正确
price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '价格'

-- ❌ 错误——浮点数有精度损失
price FLOAT NOT NULL COMMENT '价格'

floatdouble是二进制近似存储,对账经常对出几厘钱的误差。金额、汇率一旦用float存储,线上对不平只是时间问题。

③ 字符串类型:char vs varchar vs text

  • 长度几乎相等 → 用char定长
  • 长度不确定 → 用varchar,但不要超过5000
  • 超过5000的文本 → 用text独立一张表存储,避免影响主表其他字段索引效率
-- ✅ 正确——短文本
user_name VARCHAR(32) NOT NULL COMMENT '用户名'

-- ✅ 正确——超长文本独立存储
-- 主表
CREATE TABLE article (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(200) NOT NULL COMMENT '标题'
);

-- 内容独立表
CREATE TABLE article_content (
    article_id BIGINT UNSIGNED PRIMARY KEY COMMENT '文章ID',
    content TEXT NOT NULL COMMENT '文章内容'
);

④ 表必备三字段:idcreate_timeupdate_time

阿里规范强制要求每张表必备这三个字段:

-- ✅ 正确
CREATE TABLE user (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (id)
);
  • id:主键,bigint unsigned,单表时自增、步长为1
  • create_timedatetime类型,表示创建时间
  • update_timedatetime类型,表示更新时间——没有update_time的表,出了数据问题你连“什么时候改的”都查不到

三、索引规范

索引用好是“加速器”,用不好是“拖油瓶”。

索引是MySQL性能优化的核心,但索引不是越多越好。

3.1 索引设计五大原则

① 业务上具备唯一特性的字段,必须建唯一索引

即使业务层做了唯一性检查,也必须在数据库层建立唯一索引。

-- ✅ 正确
CREATE UNIQUE INDEX uk_user_name ON user(user_name);

阿里规范强调:应用层的唯一检查是不够的。只有数据库层的唯一索引才能彻底杜绝并发场景下的重复数据。

② 单表索引不超过5个

索引不是越多越好。每个索引都会影响写入性能。表数据更新时,所有索引都要同步更新。单表索引建议控制在5个以内。

③ 联合索引遵循最左前缀原则

创建联合索引时,过滤性高的字段放前面。查询条件必须从索引的最左列开始,才能命中索引。

-- 联合索引 (user_id, create_time)
-- ✅ 可以命中索引
WHERE user_id = 1 AND create_time > '2024-01-01'
WHERE user_id = 1

-- ❌ 不能命中索引
WHERE create_time > '2024-01-01'

④ 禁止在更新频繁、区分度不高的列上建索引

状态、类型等低基数列上建立索引,MySQL的优化器大概率也不会使用。不仅浪费存储空间,还会拖慢写入性能。

⑤ 禁止在索引列上进行数学运算和函数运算

一旦对索引列做了运算,索引直接失效

-- ❌ 错误——索引列参与运算,索引失效
SELECT * FROM user WHERE YEAR(create_time) = 2024;

-- ✅ 正确——对等号另一侧做运算
SELECT * FROM user WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

3.2 三大典型索引错误

错误场景后果正确做法
字段类型不一致索引失效,全表扫描JOIN字段类型必须绝对一致
对索引列做函数运算索引失效对值做运算,不对列做运算
隐式类型转换索引失效保持字段类型与查询值类型一致

一个真实案例:一张2000万行的表,WHERE status = ?跑了8秒。原因说出来你可能不信——status字段是varchar,代码里传了个int,MySQL一隐式转换,索引直接作废。这种坑,MySQL开发规范里几乎每条都在提醒你

四、SQL编写规范

让我们写出“会说话”的SQL。

4.1 查询规范

① 禁止使用SELECT *

需要哪些字段就明确写明哪些字段。

SELECT *会返回所有列,浪费网络带宽和内存,而且无法利用覆盖索引优化。

-- ❌ 错误
SELECT * FROM user WHERE id = 1;

-- ✅ 正确
SELECT id, user_name, email FROM user WHERE id = 1;

② 超过三个表禁止JOIN

阿里规范明确规定:超过三个表禁止JOIN

JOIN会消耗大量内存,产生临时表。

-- ❌ 错误——超过3张表JOIN
SELECT * FROM a JOIN b ON a.id = b.a_id 
JOIN c ON b.id = c.b_id 
JOIN d ON c.id = d.c_id;

-- ✅ 正确——拆分成多次查询,在应用层组装
SELECT * FROM a WHERE ...
SELECT * FROM b WHERE a_id IN (...)
SELECT * FROM c WHERE b_id IN (...)

③ JOIN字段必须有索引

被关联的字段必须要有索引。

而且JOIN字段的数据类型必须绝对一致。类型不一致会导致索引失效。

④ 避免在数据库中做运算

MySQL不擅长数学运算和逻辑判断。能把运算放到应用层的,坚决不要放在SQL里。

4.2 数据类型选择要点

数据类型规范要求
整数无负数用unsigned,能扩大表示范围
小数一律用decimal,禁止float/double
时间datetimetimestamp
金额decimal类型,不丢失精度
字符集统一使用utf8mb4

4.3 三大“避免”

原则原因
避免count(*)大数据量下性能差
避免使用NULL字段索引可能失效、统计可能异常
避免大SQL、大事务、大批量容易拖垮数据库

五、ORM映射规范

它是Java层的“最后一公里”。

5.1 字段映射规则

数据库布尔字段叫is_xxx,POJO类里的布尔变量不能加is前缀

// ❌ 错误——布尔变量加了is前缀
@Data
public class UserDO {
    private Boolean isDeleted;  // 序列化可能失败
}

// ✅ 正确——不加is前缀
@Data
public class UserDO {
    private Boolean deleted;    // 在resultMap中映射 is_deleted → deleted
}
<!-- resultMap映射 -->
<resultMap id="userMap" type="UserDO">
    <result column="is_deleted" property="deleted"/>
</resultMap>

5.2 逻辑删除 vs 物理删除

大厂普遍推荐逻辑删除而非物理删除。

维度物理删除逻辑删除
数据可追溯❌ 不可恢复✅ 可追溯操作记录
唯一性约束无冲突需处理唯一键复用
存储占用省空间多占一行标记
查询复杂度简单每处WHERE都带is_deleted
适用场景临时表、可重建数据核心业务数据、需审计

逻辑删除的好处是数据可追溯,坏处是原本唯一的键可能不唯一。需要根据业务场景另行处理。

六、一张图看懂大厂数据库规范全景

image

七、数据量阈值参考

阈值规范要求
单表行数 > 500万行推荐分库分表
单表容量 > 2GB推荐分库分表
预计3年内达不到不建议提前分库分表

八、优缺点

优点

1. 代码可维护性大幅提升

统一的命名规范让团队成员不需要额外沟通就能理解表结构和字段含义。一个新人入职,看表名就知道是干什么的。

2. 性能问题大幅减少

索引规范、SQL规范从源头杜绝了慢查询隐患。阿里规范里大量使用“强制”条款,正是因为阿里经历过无数双十一的极限考验。

3. 数据安全有保障

逻辑删除保证数据可追溯,唯一索引保证数据不重复,decimal保证金额不丢失精度。

4. 团队协作效率高

统一的规范让Code Review有据可依,DBA有章可循,开发有规可守。

5. 问题排查有迹可循

规范的字段注释、统一的索引命名、必备的时间字段,让线上问题排查有迹可循。

缺点

1. 规范需要工具支撑

没有工具强制落地,规范就是一张废纸。需要配合SQL审核工具、CI门禁等强制校验。

2. 初期有适应成本

团队从“自由模式”切换到“规范模式”,前几周会有一些不适应。

3. 需要结合实际场景

规范是“通用指南”,不是“铁律”。某些极端场景下可能需要适当调整,但必须在充分理解规范原理的前提下

4. 阿里和字节的风格差异

维度阿里巴巴字节跳动
设计哲学偏保守,强调稳定性偏灵活,强调开发效率
约束强度“强制”条款多“推荐”类建议占比更高
命名长度严格执行32字符限制建议“尽量简短”但无硬性约束
主键类型强制bigint unsigned推荐根据实际范围选择

两者没有绝对的对错,要根据团队规模和业务特点选择适合的规范。

更多项目实战在Java突击队网:susan.net.cn/project

九、写在最后

回到最初的问题:大厂为什么如此重视数据库规范?

因为数据库是整个系统中最“改不动”的层

代码可以重构,架构可以演进,但数据库表一旦上线,字段名、字段类型基本就改不动了。

每一行DDL都值得你认真写

阿里规范里大量使用“强制”条款,是因为阿里经历过无数双十一的极限考验,深知数据库出问题意味着什么。

字节的规范更灵活,是因为快速迭代的产品文化需要更多自主决策空间。

不管是哪种风格,核心目标都是一样的:让数据库经得起时间的考验

如果你现在才开始重视数据库规范,建议从三件事做起:

第一步:统一命名规范。表名、字段名全小写+下划线,布尔字段用is_xxx,小数用decimal字段名一旦上线被业务引用,改起来牵一发动全身

第二步:强制每张表有idcreate_timeupdate_time。没有update_time的表,出了问题你连“什么时候改的”都查不到。

第三步:用工具强制落地。SQL审核工具、CI门禁、Code Review——确保每一条SQL都经过检查才能上线。

最值钱的一条经验:表一旦上线,字段名和类型基本就改不动了。所以每一行DDL都值得你认真写。