数据库建模工具选型:从ER图到DDL导出与变更管控

发布时间:2026/10/29 3:29:21
数据库建模工具选型:从ER图到DDL导出与变更管控

数据库建模这件事,我踩过的坑比写过的表还多。需求评审会上产品说“就加个字段”,开发说“改一下就行”,等到上线发现生产库那张表连主键都没有,字段类型还停留在三年前某次紧急修复的版本上。后来我养成一个习惯:任何涉及表结构的需求,先把图画出来。DrawDB、Erd-Editor、ERD Online、CloudDM、Valentina Studio 这五个名字经常被打包扔进同一个问题里问“选哪个”,但它们压根不在同一条赛道上——一个是浏览器里打开就能画的轻量建模器,一个是长在代码编辑器里的插件,一个是面向团队的在线协同平台,一个是管数据库变更流程的管控平台,还有一个是跨库的桌面客户端。这篇就把它们按“画图、协作、落地、管控、排查”这条链路拆开,讲清楚每个工具在什么位置发力、边界在哪、实际用起来会撞上什么问题,顺带把 ER 建模里那些真正容易画错的地方掰开揉碎说一遍。

1. 需求拆解:五个工具站在建模链路的不同工位上

1.1 从一张草图到线上库,中间隔着五道工序

很多人对“数据库建模”的理解就是画一张带方框和连线的图,图往文档里一贴,任务就算完成。真实链路要长得多:需求评审阶段先出一个概念模型,只谈业务实体和它们之间的关系,不谈类型不谈索引;评审通过后转成逻辑模型,补齐主键、外键、基数、可选性;再往下是物理模型,落到具体数据库方言,字段类型、字符集、索引、分区、注释全都要定下来;物理模型要经过审核才能变成 DDL 打到测试库、预发库、生产库;上线之后还要能反向把线上结构拉回来做核对。这五道工序对应五种完全不同的能力需求,把它们塞进一个工具里做,结果通常是什么都做不好。画图的时候需要的是快、轻、随手可用,改一个字段不该等三分钟加载;协作的时候需要的是权限、版本、评论,谁改了哪张表要能追;落地的时候需要的是 DDL 方言准确、能正向导出能反向导入;管控的时候需要的是审批流、SQL 审核规则、回滚脚本、审计日志;排查的时候需要的是能同时连上五个不同环境的库,跑一条 SQL 看看数据到底长什么样。

把这五件事混为一谈,就会出现很典型的错配:拿在线协同平台去画一次性的草图,为了开个权限走半天流程;或者拿桌面客户端当团队模型库,图存在某个人电脑的 D 盘里,人一走模型就失传。我见过最离谱的一次,是某团队把 ER 图做成 PPT 存在共享盘,文件名后缀是“最终版_真的最终版_修改2”,三个月后没人说得清订单表和退款表到底是一对多还是多对多。所以选型的第一步不是比功能,是先承认这条链路上有五个工位,然后判断自己缺的是哪一环。

1.2 选型先问三个问题,答案基本就定了

第一个问题:这张图最终交付给谁。如果只是自己梳理思路,或者给两三个同事看一眼确认字段,那么轻量、零配置、打开就画是压倒性优势,DrawDB 这类浏览器工具就是为这个场景生的。如果图要进代码仓库、要跟着版本走、要能被 review,那答案就变成了长在编辑器里的 Erd-Editor。如果图要给产品、测试、运维、外部对接方一起看,还要控制谁能改谁能看,那就得是 ERD Online 这种中心化平台。

第二个问题:模型改得勤不勤。一次成型的稳定模型和一周改三次的活跃模型,对工具的要求完全不同。活跃模型最怕的是“改完忘了同步”,图上一套、库里一套。这时候版本管理和 diff 能力比画图体验重要得多,能存成文本、能进 Git、能看出一行改动的工具,价值远大于画得漂亮的工具。

第三个问题:生产库谁动、怎么动。这个问题决定了你要不要引入 CloudDM 这类管控平台。如果团队小、数据库就一个、改表的人自己也负责背锅,那直接连上去改没人拦你;一旦涉及多环境、多人、有审计要求,手工改表就是事故温床,必须把 DDL 变成有单号、有审核、有回滚的变更单。

注意:工具选型最忌讳“全家桶思维”。五个工具不是竞品关系,强行选一个替代全部,最后一定是每个环节都别扭。

2. 五款工具的能力对照与定位差异

2.1 一张表看清各自的能力边界

先把定位摆清楚,后面所有的实操才有意义。下面这张表是我自己梳理的版本,评价维度按实际使用频率排序,而不是按厂商宣传的功能清单。

工具形态模型存放位置正向导出 DDL反向导入多人协作适合的典型场景
DrawDB浏览器网页应用浏览器本地存储,可手动导出文件支持多种方言支持导入 SQL 生成图弱,靠文件传递个人梳理、快速草图、方案评审配图
Erd-Editor编辑器插件工作区文件,跟随代码仓库支持支持中等,靠 Git 分支合并研发主导的模型,要求图随代码走
ERD Online在线平台服务端模型库支持支持强,权限与版本跨角色共享、长期维护的模型资产
CloudDM数据库管控平台不管模型,管变更由变更单生成结构对比强,工单与审批生产库结构变更、SQL 审核、数据脱敏
Valentina Studio桌面客户端不入库,本地连接配置支持支持无多库连接、结构对比、数据核对、临时排查

从表里能看出一个关键分野:前三个是“建模工具”,核心产出是图和模型文件;后两个是“数据库操作工具”,核心产出是对数据库的动作。把它们放在一起比“哪个更好”没有意义,就像问螺丝刀和电钻哪个更好用。真正的决策是:你的团队现在缺的是“把想法画清楚”的能力,还是“把改动安全落地”的能力。

DrawDB 最大的优势是零门槛。打开网页,左边拖实体,右边改字段,连线选基数,十分钟能出一张能看的图。它把模型存在浏览器本地,这意味着你不用注册、不用配置、不用担心公司网络策略拦掉什么。代价也很明确:换台电脑就没了,清一次浏览器缓存可能就没了。所以用它的人必须养成一个铁律——每次收工前导出模型文件,扔进项目仓库或者云盘。这条规矩听起来简单,实际执行率不到一半,我见过太多人第二次打开发现画布空空如也的表情。

Erd-Editor 的思路完全相反,它把模型文件当成代码资产。图存在工作区里,跟着提交走,谁在哪个分支改了什么字段,用 Git 的 diff 一眼就能看出来。这个特性在需要 review 的团队里价值极高,因为模型变更和代码变更可以放在同一个合并请求里审。它的短板是依赖编辑器环境,非技术角色基本用不了,产品经理打开那个界面会直接放弃。

ERD Online 解决的是“多人共同维护一份模型”的问题。模型在服务端,有权限控制,谁改了哪张表留痕,新同事进来直接看最新版,不用挨个问“你那儿有最新的图吗”。它的代价是需要部署和维护,小团队为了三张表去搭一套服务,性价比不高。

CloudDM 严格来说不参与建模,它管的是模型落地之后的变更过程。一条建表语句从提交到执行,中间要过规则校验、要有人审批、要留回滚脚本、要记录执行人,这些能力靠人工纪律是维持不住的,必须靠平台。Valentina Studio 则更像一把万能扳手,连 MySQL、PostgreSQL、SQLite 都行,看结构、比差异、导数据、跑查询,临时排查的时候非常好用,但它不承担团队协作和长期资产管理的职责。

2.2 三种典型团队该怎么组合

三人以下的小团队,我建议 DrawDB 加 Valentina Studio 就够了。平时用 DrawDB 画模型、导 DDL,导出文件扔仓库;线上出问题用 Valentina Studio 连上去看结构和数据。不需要部署任何服务,成本几乎为零。唯一要守的规矩是模型文件必须进仓库,不能只存在浏览器里。

五到二十人的研发团队,Erd-Editor 加 CloudDM 是比较顺的组合。模型跟着代码走,谁提的合并请求谁负责更新图;所有打到测试库以上环境的 DDL 走 CloudDM 的变更单,规则里配上“禁止无主键”“禁止 TEXT 字段建普通索引”“DROP 语句必须人工复核”这类硬约束。这套组合的好处是责任清晰,代码评审和模型评审合一,变更评审和上线审批合一。

跨部门、有外部对接方的场景,ERD Online 加 CloudDM 更合适。前者让产品、测试、外部方都能看到同一份最新模型,后者保证变更可控。DrawDB 在这个规模下依然有用,但它退化成“个人草稿纸”的角色,正式模型必须收敛到平台上去。

3. ER 建模的核心细节:那些图上看不出来但会炸的地方

3.1 主键设计:自增 ID 和业务编号要分家

新手画 ER 图最爱犯的错,是把业务单号当主键。订单号看着唯一、看着稳定,拿来当主键似乎天经地义。问题在于业务编号是会变的——规则调整、前缀更换、分库后需要加路由位,一旦主键变了,所有引用它的外键全要跟着改,这在有数据的生产库上基本等于重建。正确做法是双轨制:物理主键用无业务含义的自增 BIGINT,业务编号单独建唯一索引。

字段类型上我现在的默认选择是BIGINT UNSIGNED AUTO_INCREMENT。用 INT 的话上限约 21 亿,订单类表在中等规模业务里两三年就能摸到边,到时候改类型是一次全表重建,锁表时间难以接受。BIGINT 多占 4 字节,换来的是永远不用考虑这个问题。这里有个常被忽略的细节:自增主键在分布式写入场景下会冲突,如果未来有分库分表的可能,主键要么用雪花算法生成的 BIGINT,要么在建模阶段就把分片键设计进去,别等拆库的时候再返工。

还有一类表压根不需要自增主键,比如纯关联的中间表、配置项表。中间表的主键通常就是两个外键的联合主键,配上唯一约束,天然防重复。这时候要注意联合主键的字段顺序:把区分度高的字段放前面,InnoDB 的聚簇索引按这个顺序组织数据,顺序选对了范围查询会快很多。

提示:主键的选择在图上就是一个勾选框,但它决定了未来三到五年这张表的扩展性。画图的时候多花三十秒想一下,能省掉后面几天的事。

3.2 关系基数:一对多好画,多对多和一对一容易埋雷

一对多是最好处理的,多的那方加个外键字段就行。麻烦的是多对多。图上画一条带两个鸦爪的线很轻松,落到物理模型必须有中间表,而且中间表往往不是“两个外键”这么简单——它经常承载业务属性,比如用户和角色的关联表里会有授权时间、授权人、失效时间,商品和标签的关联表里会有排序权重。这些字段在概念模型阶段就该识别出来,否则到了开发阶段才发现中间表需要主键以外的结构,模型又得推翻重来。

一对一是另一个高频误区。很多人一看“一个用户对应一个用户详情”,就直接把详情字段塞进主表。这在字段少的时候没问题,但一旦详情里有大文本、有低频访问的长字段,主表行宽会迅速膨胀,影响缓冲池命中率。物理层面的合理做法是拆表,用同一个主键值关联,形成垂直分割。图上表现为两个实体一对一连线,实现上是主键共享而不是额外加外键列。

外键要不要建物理约束,这个争论十几年了。互联网业务的主流做法是不建物理外键,靠应用层保证一致性,原因是物理外键在高并发写入时会带来额外的锁检查和级联开销,而且分库分表之后跨库外键根本没法维护。但请注意,不建物理外键不等于不画关系线。ER 图里的关系表达的是业务语义,物理模型里可以只有索引没有约束,这两件事要分开。如果在 DrawDB 或者 Erd-Editor 里画了关系却导出成没有外键的 DDL,记得在文档里写清楚“逻辑外键,应用层保证”,否则接手的人会以为是导出工具出了 bug。

3.3 数据类型与字符集:跨库迁移时最先爆炸的地方

字段类型这件事,在一个库上待久了会形成肌肉记忆,一旦要跨库迁移就原形毕露。举几个实际会撞上的点。

布尔类型。MySQL 里没有原生 boolean,TINYINT(1) 是约定俗成,PostgreSQL 有真正的 boolean。图上一画“是否删除”,导出到两种方言就要做映射,有些工具会自动处理成 TINYINT 和 BOOLEAN,有些会统一导出成 INT,后者在 PostgreSQL 上也能跑,但语义就丢了。

时间类型。DATETIME不带时区,TIMESTAMP带时区转换且范围只到 2038 年。订单创建时间这种要长期保存的字段,我倾向用DATETIME(3)存 UTC 时间,毫秒精度够用,业务侧做时区转换。用 TIMESTAMP 的话,2038 年问题虽然遥远,但迁移到其他数据库时行为差异会让人抓狂。精度也别忽略,DATETIME默认精度是秒,高并发下同一秒内的多条记录排序会乱,加个(3)就解决了。

金额类型。FLOAT和DOUBLE绝对不能用于金额,二进制浮点表示不了 0.1。DECIMAL(18,2)是稳妥选择,18 位总长度、2 位小数,单条记录最大到千万亿级别,够任何业务用。有些团队用 BIGINT 存“分”,也是一种做法,好处是运算全是整数没有精度问题,坏处是每次展示都要除 100,容易漏。

字符集。utf8mb4 是现在的默认答案,utf8(三字节)存不了四字节字符,包括部分生僻字和表情符号,用户昵称里带一个就能让插入报错。建库建表统一 utf8mb4,排序规则按数据库版本选。这里有个实操细节:InnoDB 早期版本单列索引长度上限 767 字节,utf8mb4 下单列索引最多 191 个字符(191×4=764),超过就得用前缀索引或者改行格式。现在 MySQL 8 默认 DYNAMIC 行格式,上限提到 3072 字节,utf8mb4 下能索引 768 个字符,但如果你要兼容老版本,VARCHAR(255)建索引依然会报错,建模阶段就得把长度压到 191 以内,或者明确改用前缀索引。

枚举类型。用ENUM存状态看着优雅,实际是个坑:加一个枚举值要改表结构,而且是 DDL,大表上执行会锁。用TINYINT加应用层常量映射是更稳的做法,加状态只需要改代码,数据库层面零变更。ER 图里把状态字段的取值含义写进字段注释,比什么都强——注释是唯一能在数据库里和代码一起演进的文档。

4. 实操:用 DrawDB 从零建一个订单模型并导出 DDL

4.1 建模前的准备与画布约定

动手之前先定几条规矩,不然后面图会乱成一锅粥。第一,命名统一,表名蛇形小写下划线分隔,字段名同理,主键统一叫id,外键叫关联表名_id,时间字段统一created_at、updated_at。第二,所有表必须有主键,所有字段必须有注释,注释用中文写清楚业务含义和取值范围。第三,图上的实体按业务域分组摆放,用户相关放一块、交易相关放一块、商品相关放一块,连线尽量不交叉。这三条看着是形式主义,实际决定了这张图三个月后还有没有人愿意打开看。

我一般会在画布上先摆四个核心实体:客户、订单、订单明细、商品。先不急着加字段,先把它们之间的关系连起来——客户对订单是一对多,订单对订单明细是一对多,商品对订单明细是一对多。连完看一眼整体形状,确认没有遗漏的实体。这个阶段刻意保持粗糙,因为概念模型的任务是确认边界,不是确认字段。

实体摆好之后开始填字段。客户表:主键、客户编号、手机号、昵称、状态、创建时间、更新时间。订单表:主键、订单号、客户 ID、订单状态、应付金额、实付金额、下单时间、支付时间。订单明细:主键、订单 ID、商品 ID、购买数量、成交单价、小计金额。商品表:主键、商品编码、商品名称、售价、库存、上下架状态。填的时候注意每个字段都要标类型和是否可空,这两项在后面的 DDL 导出里会直接影响结果。

4.2 从图到 DDL:导出的语句要人工过一遍

画完在导出菜单里选目标方言,拿到建表脚本。下面是这个模型导出的核心部分,我做了人工整理,补齐了注释和索引命名:

CREATE TABLE `customer` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `customer_no` VARCHAR(32) NOT NULL COMMENT '客户编号,业务唯一', `mobile` VARCHAR(20) NOT NULL COMMENT '手机号,脱敏存储', `nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0禁用 1正常', `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_customer_customer_no` (`customer_no`), KEY `idx_customer_mobile` (`mobile`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'; CREATE TABLE `orders` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `order_no` VARCHAR(32) NOT NULL COMMENT '订单号,业务唯一', `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', `order_status` TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态:0待支付 1已支付 2已发货 3已完成 4已取消', `pay_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '应付金额', `paid_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '实付金额', `ordered_at` DATETIME(3) NOT NULL COMMENT '下单时间', `paid_at` DATETIME(3) NULL COMMENT '支付时间', `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_orders_order_no` (`order_no`), KEY `idx_orders_customer_id_ordered_at` (`customer_id`, `ordered_at`), KEY `idx_orders_status_ordered_at` (`order_status`, `ordered_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';

导出之后有三件事必须人工过一遍。第一,索引。工具只会按你在图上勾选的字段生成单列索引,但真实查询往往是组合条件,比如“某个客户的最近订单”需要(customer_id, ordered_at)联合索引,字段顺序错了索引就白建。第二,默认值。所有 NOT NULL 字段都要有明确默认值,否则以后加字段会锁表或者报错。第三,注释。工具的注释字段经常是空的,而注释是唯一能跟数据库一起活下来的文档,宁可多写二十个字。

索引这里补一个计算过程,说明为什么有的字段不适合建索引。假设订单表有一列remark VARCHAR(500)存备注,你要按备注模糊查,直接对它建普通索引,utf8mb4 下索引长度 500×4=2000 字节,超过了老版本 767 字节上限,会直接报错;即使新版本能建,全模糊查询LIKE '%关键词%'也用不上 B+ 树索引。这种情况的正确解法是前缀索引加上限定长度的查询,或者老老实实上全文索引/外部检索引擎,而不是硬在 ER 图里点一个索引勾。

4.3 反向工程:把老库拉回图里再谈重构

接手一个陌生库的时候,正向建模没用,得先反向。DrawDB 支持导入 SQL 脚本生成图,具体做法是用客户端把目标库的 DDL 导出来——注意只导结构不导数据,脚本可能几十兆,导数据的部分要剔掉。导入之后你会发现图自动排得很乱,连线交叉得很厉害,这时候手动挪一挪位置,按业务域重新分组。这个过程本身就是理解库结构的最好方式,挪到某张表的时候你会自然去问“这张表和订单是什么关系”,问着问着业务模型就清楚了。

反向导入有个陷阱:老库往往没有外键,工具只能靠字段名去猜关系,猜错了图就是错的。比如user_id指向的可能不是用户表而是操作人表。我的做法是导入之后先关掉自动连线,手动一张张确认,确认一张锁一张,避免后面反复被自动布局打乱。这个过程慢,但比在错误的图上做重构决策要快得多。

5. 协作与版本:Erd-Editor 和 ERD Online 的两种解法

5.1 Erd-Editor:让 ER 图变成可以 review 的代码资产

这个工具的价值只有经历过“图版本混乱”的人才体会得到。模型文件存在工作区,本质是一个结构化的文本文件,提交到仓库之后,任何人改一个字段类型,都会在合并请求里显示成一行 diff。评审的时候看到“这个字段从 VARCHAR(64) 改成 VARCHAR(128),原因是手机号要支持国际区号”,比看一张截图强一百倍。

实操上有个约定值得推广:模型变更必须和 DDL 变更放在同一个提交里。改图、导出 SQL、把 SQL 放进迁移脚本目录、一起提交。这样代码评审的时候能同时看到模型和语句,不会出现“图画了但语句忘了写”或者“语句改了但图没更新”的情况。分支策略上,模型文件跟着功能分支走,合并的时候如果两个人改了同一张表,Git 会冲突,这时候人工合并不是麻烦,而是一次必要的设计对齐——冲突本身就说明两个人都在动同一块结构,正好坐下来谈。

它的局限也要认。非技术角色看不懂这个界面,产品经理想确认字段含义,你得导一张图片发过去。另外大模型文件加载会慢,几百张表的库打开要等,这种时候就别指望它能替代专业建模工具了。

5.2 ERD Online:中心化模型库解决的是“谁的版本才对”

团队大了之后,最大的问题不是画图,是找图。每个人手里都有一份自己导出的模型文件,时间一长谁也不知道哪份是最新的。中心化模型库解决的正是这个问题:模型只有一份,所有人访问的都是同一个地址,改了立刻生效,历史版本可回溯。

它带来的新问题是流程成本。改一个字段要先申请权限,改完要提交审核,小改动走一遍流程可能比改代码还慢。我的建议是分级:核心业务表走严格流程,边缘表、日志表、临时表给研发自主权。权限设计上按业务域划分,交易域的模型交易组可写、其他组只读,比全局可写要安全得多。

另一个实际收益是评论功能。产品在图上的某个字段旁边留一句“这个状态是不是还缺一个退款中”,比在聊天群里发一张截图再圈红框高效得多,而且讨论记录跟着模型走,半年后还能查到当时为什么这么设计。

6. 落地管控与日常运维:CloudDM 和 Valentina Studio

6.1 CloudDM:把 DDL 关进流程里

模型画得再好,最终要变成线上的一条语句。这条语句谁执行、什么时候执行、执行失败了怎么回滚,靠人工纪律是管不住的。管控平台的核心价值就在这:所有变更必须提单,单子里带变更 SQL、影响评估、回滚语句,走完审批才能执行,执行过程留审计日志。

配置上有几条规则我建议默认打开。禁止无主键建表——这条能拦掉大量隐患,没有主键的表在数据订正时几乎无法安全操作。禁止对超过阈值的表做全表更新或删除,阈值按团队规模定,比如一百万行。禁止在业务高峰期执行大表 DDL,这个靠时间窗配置。强制要求回滚脚本,没有回滚语句的单子不允许提交。还有一条容易被忽略:结构对比。变更执行前后做一次结构快照,跑完对比一下,能发现“以为只加了个字段结果顺手删了个索引”这种事。

数据脱敏也在这一类平台里,测试环境拉生产数据之前先把手机号、身份证、地址这些字段处理掉,比事后追责有效。

6.2 Valentina Studio:临时排查的万能扳手

线上出问题的时候,你需要的是立刻连上去看数据,而不是打开一套平台走登录流程。这类桌面客户端的价值就在这里:配好连接,双击打开,跑查询,看结构,比差异,导结果。多数据库支持意味着你可以在一个界面里同时管着 MySQL 的业务库、PostgreSQL 的报表库、SQLite 的本地缓存,不用来回切工具。

几个实际好用的场景。第一,结构对比:测试库和生产库到底差在哪,拉出来一对比,少了个索引还是字段类型不一致,一目了然。第二,数据核对:两个环境同一张表按主键对比数据,找出缺失或差异的记录。第三,临时查询:写一条复杂 SQL 验证数据问题,结果直接导出成表格发给相关人。

要注意的是这类客户端不要随便配生产库的写权限。日常排查只用只读账号,需要改数据走管控平台的工单,这条规矩能挡掉大部分误操作事故。

7. 常见问题与排查技巧实录

7.1 高频问题速查表

现象大概率原因处理方式
浏览器里画的图第二天不见了模型存在本地存储,清缓存或换设备导致丢失养成收工导出模型文件的习惯,文件进仓库
导出的 DDL 在目标库执行报语法错方言选错,或目标库版本不支持该语法导出前确认目标库类型与版本,自增、注释、默认值语法逐项核对
反向导入后关系全是错的老库没有物理外键,工具按字段名猜测关闭自动连线,逐张表人工确认后锁定
建索引时报 key length 超限字符集为 utf8mb4 且索引列过长缩短列长度到 191 以内,或改用前缀索引
加字段时业务报超时大表 DDL 锁表走在线 DDL 工具或管控平台的低峰窗口执行
两个人同时改模型产生冲突缺少版本管理约定模型文件进 Git,冲突时人工对齐设计
金额计算出现 0.01 误差用了浮点类型存金额统一改 DECIMAL,或整数存分
状态字段加枚举值失败用了 ENUM 类型改用 TINYINT 加应用层常量映射

7.2 几条踩坑换来的经验

第一条,图不是画给自己看的。我早期画图只求自己看懂,字段名前缀随意,注释一个不写,三个月后自己都看不懂。后来强制自己按“接手的人完全不了解业务”的标准来画,注释写到能当需求文档读,返工率直接降下来。

第二条,模型和库必须能对得上。任何一次线上变更之后,我会花五分钟把图更新掉,哪怕只是加了个字段。这个动作看起来琐碎,但它是防止模型腐化的唯一方式。模型一旦开始和现实脱节,第二次、第三次就没人维护了,最后彻底变成历史文档。

第三条,别迷信工具的自动导出。所有工具导出的 DDL 都应该当成草稿,索引、默认值、注释、字符集这四项必定要人工过一遍。我见过导出脚本里所有 VARCHAR 都默认成了 255,结果一个存 URL 的字段上线三个月就截断报错。

第四条,小团队不要为了流程而流程。三个人两张表,非要搞一套审批流,最后大家会想办法绕过去,绕过的方式往往更危险。工具的复杂度应该跟着团队规模走,规模没到就先用人盯人加约定,到了再上平台。

第五条,反向工程是理解陌生系统最快的方式,但要接受它不完美。自动生成的图一定有问题,把它当成起点而不是答案,挪一遍、确认一遍,你对这个库的理解会比读十页设计文档都深。

最后分享一个我自己一直在用的小习惯:每张核心表在模型里都加一个“这张表解决什么问题”的说明字段,一句话就行。这句话在评审的时候经常成为关键——很多设计争议的根源,是大家对这张表到底该承担什么职责根本没达成共识。图只是形式,共识才是内容。