ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

大厂的数据库规范值得借鉴

大厂的数据库规范值得借鉴 h5打开以查看从建表到SQL优化让你少走三年弯路今天趁着打铁跟大家一起聊聊一线互联网大厂的数据库规范希望的对你会有所帮助。有些大厂几百上千人的研发团队数据库却能保持井井有条。表结构清晰、命名规范统一、索引设计合理、SQL性能可控。很多经验还是非常值得借鉴的。一、为什么大厂如此重视数据库规范在聊具体规范之前我们先理解一个根本问题——为什么大厂把数据库规范看得这么重要第一个原因数据库是“最改不动”的层。代码可以重构架构可以演进但数据库表一旦上线字段名、字段类型基本就改不动了。改一个字段名所有依赖它的业务代码都要改而且没法预发布测试。每一行DDL都值得你认真写。第二个原因数据量是“指数级”增长的。一个“先上线再说”的表可能三个月就涨到几百万行。等发现问题再来优化成本是当初规范的十倍甚至百倍。第三个原因数据库是整个系统的“命根子”。代码出问题最多功能不能用。数据库出问题整个系统直接瘫痪。阿里规范里大量使用“强制”条款正是因为阿里经历过无数双十一的极限考验深知数据库出问题意味着什么。说白了数据库规范不是用来“管人”的是用来“保命”的。二、建表规约从第一行DDL就决定了生死。2.1 命名规范它是最容易被忽略、又最难改的部分。① 表名、字段名必须全小写禁止大写阿里规范强制要求表名、字段名必须使用小写字母或数字禁止出现数字开头。-- ✅ 正确 CREATE TABLE user_info (...); CREATE TABLE order_detail (...); -- ❌ 错误——包含大写字母 CREATE TABLE UserInfo (...); CREATE TABLE OrderDetail (...);为什么要全小写MySQL在Windows下不区分大小写但在Linux下默认是区分大小写的。一旦部署到Linux环境System和system就是两张不同的表。用全小写彻底避免这个坑。② 表名单数形式禁止复数表名表示实体内容不是实体数量。-- ✅ 正确 CREATE TABLE user (...); CREATE TABLE order (...); -- ❌ 错误——用了复数 CREATE TABLE users (...); CREATE TABLE orders (...);③ 禁用保留字不能使用desc、range、match、delayed等MySQL保留字作为表名或字段名。④ 索引命名统一索引类型命名格式示例主键索引pk_字段名pk_id唯一索引uk_字段名uk_user_name普通索引idx_字段名idx_create_time看名字就知道索引类型排查问题时省一半力气。⑤ 表名长度不超过32字符库名、表名、字段名最好不超过32个字符做到“见名知意”即可。2.2 字段规范——选对类型事半功倍① 布尔字段is_xxxunsigned 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禁止float和double这是金钱相关业务的“铁律”。-- ✅ 正确 price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 价格 -- ❌ 错误——浮点数有精度损失 price FLOAT NOT NULL COMMENT 价格float和double是二进制近似存储对账经常对出几厘钱的误差。金额、汇率一旦用float存储线上对不平只是时间问题。③ 字符串类型charvsvarcharvstext长度几乎相等→ 用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 文章内容 );④ 表必备三字段id、create_time、update_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单表时自增、步长为1create_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代码里传了个intMySQL一隐式转换索引直接作废。这种坑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时间用datetime或timestamp金额decimal类型不丢失精度字符集统一使用utf8mb44.3 三大“避免”原则原因避免count(*)大数据量下性能差避免使用NULL字段索引可能失效、统计可能异常避免大SQL、大事务、大批量容易拖垮数据库五、ORM映射规范它是Java层的“最后一公里”。5.1 字段映射规则数据库布尔字段叫is_xxxPOJO类里的布尔变量不能加is前缀。// ❌ 错误——布尔变量加了is前缀 Data public class UserDO { private Boolean isDeleted; // 序列化可能失败 } // ✅ 正确——不加is前缀 Data public class UserDO { private Boolean deleted; // 在resultMap中映射 is_deleted → deleted }!-- resultMap映射 -- resultMap iduserMap typeUserDO result columnis_deleted propertydeleted/ /resultMap5.2 逻辑删除 vs 物理删除大厂普遍推荐逻辑删除而非物理删除。维度物理删除逻辑删除数据可追溯❌ 不可恢复✅ 可追溯操作记录唯一性约束无冲突需处理唯一键复用存储占用省空间多占一行标记查询复杂度简单每处WHERE都带is_deleted适用场景临时表、可重建数据核心业务数据、需审计逻辑删除的好处是数据可追溯坏处是原本唯一的键可能不唯一。需要根据业务场景另行处理。六、一张图看懂大厂数据库规范全景七、数据量阈值参考阈值规范要求单表行数 500万行推荐分库分表单表容量 2GB推荐分库分表预计3年内达不到不建议提前分库分表八、优缺点优点1. 代码可维护性大幅提升统一的命名规范让团队成员不需要额外沟通就能理解表结构和字段含义。一个新人入职看表名就知道是干什么的。2. 性能问题大幅减少索引规范、SQL规范从源头杜绝了慢查询隐患。阿里规范里大量使用“强制”条款正是因为阿里经历过无数双十一的极限考验。3. 数据安全有保障逻辑删除保证数据可追溯唯一索引保证数据不重复decimal保证金额不丢失精度。4. 团队协作效率高统一的规范让Code Review有据可依DBA有章可循开发有规可守。5. 问题排查有迹可循规范的字段注释、统一的索引命名、必备的时间字段让线上问题排查有迹可循。缺点1. 规范需要工具支撑没有工具强制落地规范就是一张废纸。需要配合SQL审核工具、CI门禁等强制校验。2. 初期有适应成本团队从“自由模式”切换到“规范模式”前几周会有一些不适应。3. 需要结合实际场景规范是“通用指南”不是“铁律”。某些极端场景下可能需要适当调整但必须在充分理解规范原理的前提下。4. 阿里和字节的风格差异维度阿里巴巴字节跳动设计哲学偏保守强调稳定性偏灵活强调开发效率约束强度“强制”条款多“推荐”类建议占比更高命名长度严格执行32字符限制建议“尽量简短”但无硬性约束主键类型强制bigint unsigned推荐根据实际范围选择两者没有绝对的对错要根据团队规模和业务特点选择适合的规范。九、写在最后回到最初的问题大厂为什么如此重视数据库规范因为数据库是整个系统中最“改不动”的层。代码可以重构架构可以演进但数据库表一旦上线字段名、字段类型基本就改不动了。每一行DDL都值得你认真写。阿里规范里大量使用“强制”条款是因为阿里经历过无数双十一的极限考验深知数据库出问题意味着什么。字节的规范更灵活是因为快速迭代的产品文化需要更多自主决策空间。不管是哪种风格核心目标都是一样的让数据库经得起时间的考验。如果你现在才开始重视数据库规范建议从三件事做起第一步统一命名规范。表名、字段名全小写下划线布尔字段用is_xxx小数用decimal。字段名一旦上线被业务引用改起来牵一发动全身。第二步强制每张表有id、create_time、update_time。没有update_time的表出了问题你连“什么时候改的”都查不到。第三步用工具强制落地。SQL审核工具、CI门禁、Code Review——确保每一条SQL都经过检查才能上线。h5打开以查看
返回列表