资讯动态

数据库设计Step by Step:以博客系统为例的完整实战指南

发布时间:2026/9/28 13:25:16 来源:尧图企业网站定制
拿到“数据库设计 Step by Step”这个题目我第一反应是又一个新手要被网上那些零散教程带偏了。干这行这么多年见过太多人拿到需求后第一时间打开 Navicat 就开始建表等业务一到深处才发现问题然后陷入“改表结构 → 迁移数据 → 改代码 → 又发现新问题”的循环里。这个系列“扬帆启航”的第一篇我就带你从零开始走一遍数据库设计的完整路径需求分析、概念设计、逻辑设计、物理设计、评审与迭代。这篇内容不是给你背理论的而是给你一套可以直接落地的思考方法和实操套路。全文以“博客系统”作为贯穿案例重点讲清楚一边写 SQL 之前该如何想清楚你的表结构。适合刚接触数据库的学生、转行做后端的同学以及那些已经会写 SQL 但没系统学过如何做设计的开发人员。1. 拿到需求先别动手数据库设计的第一步是搞清楚业务逻辑这一步被绝大多数新手跳过却是整个设计过程最关键的环节。你跳过它后面每一步都是空中楼阁。1.1 为什么上来就建表会翻车很多人以为数据库设计的核心是 SQL 语法是建表语句写得漂亮。实际上SQL 只是最后一公里的翻译工作真正的设计早在写任何 CREATE TABLE 之前就已经开始了。你连业务都没搞明白建出来的表一定是“你以为的”业务而不是“真实存在”的业务。举个例子。你脑子里以为博客系统只需要“用户表、文章表、评论表”三张表就够了。等产品经理走过来告诉你文章要打标签评论要支持点赞用户要能看到浏览历史这时候你会发现自己设计的表根本顶不住。硬加字段表越来越宽越来越乱。重构表线上数据怎么办代码联动怎么改没有想清楚业务就动手后面全是在为自己的冲动买单。我踩过这种坑。当年做第一个项目心想“我写过增删改查建表还不简单”直接拍了拍脑袋建了 8 张表。结果第四周产品提需求数据库改了 3 轮代码改了 5 轮最后项目延期锅全在“需求变太多”。现在回头看需求确实有变化但 90% 的变数在一开始梳理业务时就能预判到。1.2 需求分析到底要收敛哪些信息做需求分析不是去开会记纪要而是要围绕“谁、什么、在哪、何时、为什么、怎么”六个方向把业务拆干净。我最常用的方法是五步收敛法。第一找角色。系统里有哪几类用户博客系统里至少有普通读者、注册用户、作者、管理员。第二找对象。业务里有哪些核心名词用户、文章、评论、分类、标签、点赞记录。第三找动作。角色对对象做了什么作者发布文章读者评论文章用户点赞评论管理员审核文章。第四找规则。每个动作有什么限制一个用户可以有多篇文章一篇文章可以有多个标签一个评论只能属于一篇文章。第五找指标。哪些数据需要被统计权衡文章的阅读数、评论数、点赞数这些指标是否需要实时精确还是允许一定延迟。这四个维度整理下来你手里的就不再是笼统的需求文档而是一张清晰的数据清单。别小看这种填空式梳理它能强迫你把“大概差不多”变成“确定就是”。我习惯把梳理结果画成一张简单的表格横轴写实体纵轴写关系一格一格填填不出来的部分就是还没搞懂的点。1.3 把业务拆成“实体 属性 关系”的最小清单做完上面的梳理你手上应该有一堆零散信息。接下来把它们整理成三张清单。实体清单就是那些需要长期存储的核心名词。博客系统的实体有用户、文章、评论、分类、标签。属性清单就是每个实体有哪些值得存储的特征。用户的属性有手机号、密码、昵称、头像文章的属性有标题、正文、状态、发布时间。关系清单就是实体与实体之间的连接方式。用户和文章是“一对多”文章和标签是“多对多”文章和评论是“一对多”。我给你一张实际可抄作业的表你想想自己在做项目的时候是不是这么列出来的实体关键属性与其他实体关系用户user_info手机号、昵称、密码哈希、头像一对多文章、评论、点赞文章article标题、正文、状态、发布时间属于用户一对多评论多对多标签评论comment评论内容、评论时间属于用户、属于文章一对多少点赞标签tag标签名、标签描述多对多文章点赞like_record点赞时间、点赞对象属于用户多态文章或评论你注意这里面的“用户”设计成了 user_info 而不是 user是因为 user 在很多数据库里是关键字或者保留字能避开就尽量避开。这属于命名规范后面我还会细说。1.4 确定范围第一版先做最小闭环我刚做设计的时候特别贪婪总想把所有功能都考虑进去结果就是设计出来的表像八爪鱼一样什么都挂一点什么都做不深。后来慢慢领悟到一个道理数据库设计和产品迭代一样需要考虑 MVP先做最小可用版本。第一版博客系统你只需要用户能注册登录、作者能发布文章、读者能浏览文章并评论这三件事跑通就够了。点赞、关注、消息通知、模板消息这些可以等主流程稳定后再补。每个功能进入设计都要问自己一句现在不做它会死吗答案是不会那就划到下一期。但“划到下一期”不等于不考虑。比如你知道用户未来会有收货地址但没有设计地址表。那可以在用户表里预留一个逻辑字段不用预留本身也是过度设计。可靠的做法是本期把用户表设计得干净稳定下期要加地址表时再做独立扩展这种加法在数据库层面是很自然的。真正痛苦的加法是改已有的核心字段比如把手机号从 varchar(20) 改成 varchar(64)——那才是灾难。2. 概念模型设计用实体关系图把业务“画”出来需求清单理清之后第二步是把文字看成图形。这一步不需要写 SQL甚至连表结构都不需要细化你只需要画一张框框图。2.1 实体、属性、关系三个词的真实含义实体是名词是业务里真正需要记录的“人、事、物”。属性是实体身上的形容词或名词回答的是“这个实体长什么样子”。关系是动词是实体之间发生的关联回答的是“谁和谁之间怎么样了”。拿博客系统的文章来举例。“文章”是一个实体它有标题、正文、发布时间这些属性。“作者”也是一个实体他有昵称、头像、手机号。文章和作者之间的关系叫“撰写”一个作者能写多篇文章所以这个关系是“一对多”。听上去很简单但筛掉属性里的水货需要经验。最常见的错误是把应该独立成表的对象当成另外一个对象的属性。比如有人会把“标签”直接设计成文章表里的一个字段存成逗号分隔的字符串“标签: 后端,数据库,Python”。这样做查询“包含 Python 标签的文章”会变得异常痛苦只能用 LIKE 模糊匹配数据量一上来性能立马崩。这就是概念建模不彻底的代价。2.2 三种关系类型一对一、一对多、多对多实体之间的数量关系是概念模型最核心的内容。一对一意思是这个实体的一条记录只对应另一个实体的一条记录。比如一个用户只有一个登录账号反过来一个登录账号也只属于一个用户。这种关系最简单实际看看不需要单独建表两张表共用同一个主键或者把一方的外键放在另一方表里都行。一对多是最常见的关系。一个作者可以写多篇文章一篇文章可以被多个评论回复。实现方式是在“多”的那一方放一个“一”那一方的主键这个字段叫外键。文章表里放 author_id评论表里放 article_id就是典型设计。多对多稍微剥离它。一篇文章可以打多个标签一个标签下面可以有多篇文章。这个是三位关系在数据库里无法直接表达必须拆成两个一对多这时候就需要一张中间表。比如 article_tag 表里面只有两个字段article_id 和 tag_id各自都指向原表主键。给三种关系做个对比你在建模时直接对号入座关系类型例子实现方式是否建中间表一对一用户与登录账号共享主键/单方外键一般不需要一对多用户发文章在“多”方加外键不需要多对多文章与标签拆成两个一对多必须建中间表2.3 识别业务约束唯一、非空、默认值图形画完之后要开始给每个属性贴“限制标签”。别小看这一环节约束的准确度直接决定你的数据质量。唯一约束哪些字段不允许重复用户手机号必须唯一用户昵称是否需要唯一如果要那就在昵称字段上建唯一索引。文章标题在同一用户下是否唯一这些都要在画图阶段写下来落库的时候就能变成实际的 unique key。非空约束哪些字段必须填用户手机号不能为空密码哈希不能为空文章标题不能为空。但注意“不能为空”不代表“不给默认值”。对于状态字段默认值往往比空值更合理文章状态默认 0草稿就比默认 NULL 清晰得多。默认值哪些字段应该自动生成创建时间默认当前时间更新时间默认当前时间阅读数默认 0。这一块设计得好后面写代码时你敢大量省事甚至能在数据库层直接完成一部分初始化逻辑。2.4 给实体和字段起名字别在细节上埋雷命名规范这件事看起来没什么技术含量但实际上对后续开发的幸福感影响极大。我看到太多项目里出现“user”“users”“t_user”混用的情况每个开发都有自己的风格最后连哪张表是核心表都分不清楚。我给自己规定了三条铁律。第一表名用复数还是单数统一定一个我个人用单数user_info 比 users 更自然。第二字段名全部小写下划线禁止驼峰userName 和 user_name 混在一个项目里会让 SQL 写得极度别扭。第三避免 MySQL 保留字比如 order、group、describe、key 都要避开不然每次写 SQL 都得加反引号烦不胜烦。还有一点值得注意就是给字段起名要贴业务而不是贴实现。字段名叫 data 或者 info 这种跟没起名没什么区别。正确的是 mobile、nickname、password_hash一眼就能看出实际含义。3. 逻辑结构设计把模型翻译成关系表关键看三范式概念模型是一张设计图逻辑结构设计则是按图施工把实体和关系转化为可以在数据库里创建的表结构。这一步是很多人理解的“数据库设计”但它的正确打开方式其实是规范化。3.1 从实体到二维表一个实体一张表的转换规则转换规则其实就三条实体到表属性到字段关系变成外键或者中间表。一对一关系在任意一方放对方主键一对多关系在“多”的一方放“一”的一方主键多对多关系新建中间表。实际设计时我会把上一步整理的实体清单重新打开逐个对照生成表结构。每个实体对应一张表每个属性对应一个字段然后给每张表加两个通用字段created_at 和 updated_at。这两个字段能救命无论实际查询还是排查线上问题你都依赖时间信息。没有它们出了问题你连数据是什么时候写入的都不知道。代理主键我这里多说一句。我强烈建议你在每张业务表上设置一个与业务无关的自增主键 id而不是直接用手机号、邮箱这类业务字段做主键。业务字段做主键一旦规则变了比如用户允许改绑手机号你的主键也得跟着变连带外键引用全部要改成本极高。代理主键本身没有任何业务含义永远不变稳定可靠。3.2 主键、外键、唯一约束怎么选主键已经说过了尽量选代理主键int 或 bigint视数据量而定。小项目 int 够用做互联网产品直接上 bigint 无符号别给自己留不够用的量级风险。外键有两条路线。物理外键就是数据库层面加 FOREIGN KEY 约束保证引用完整性逻辑外键就是只在业务代码层面做关联不建数据库约束。全互联网项目的普遍做法是逻辑外键。为什么因为物理外键在删除、更新时可能锁表分布式架构下也是个大麻烦性能影响明显。我不建议你做项目时把外键约束建上但要记得在下标上建索引不然关联查询照样慢。唯一约束该用就要用这是数据脏乱差的最强防线。用户手机号必须唯一那就加独特板 key。不要相信“代码逻辑里做了唯一校验就够”的话并发场景下两个请求同时进来代码判断都通过数据库一写就是两条重复记录。真正兜底的只有唯一约束。3.3 三范式用大白话把 1NF、2NF、3NF 讲明白三范式不是考试题是实实在在让表结构变合理的规则。第一范式要求字段不可再分。在现代关系型数据库里这个基本已经默认满足了。如果你在文章表里写一个字段叫“作者信息”里面又存姓名又存手机号那就违反了第一范式。现在的做法是把作者信息拆成 author_name、author_mobile 两个字段或者干脆只存 author_id再关联用户表。第二范式要求消除部分依赖。它主要针对联合主键的情况。比如文章标签关联表 (article_id, tag_id)如果你额外存了一个 tag_name 在这个关联表里那 tag_name 就只依赖 tag_id 而不依赖 article_id这就是部分依赖违反第二范式。正确的做法是 tag_name 只存在标签表关联表只存两个 id。第三范式要求消除传递依赖。比如文章表里如果存了 category_name而分类名称由 category_id 决定那么 category_name 通过 category_id 间接依赖文章主键这就是传递依赖。解决办法也是只存 category_id需要名字时再 join 分类表。我当年学三范式的时候也觉得抽象后来发现一个更好用的判断标准如果一个字段的值可以由另一个字段推导出来那这个字段就该被删除或者挪到别的表里去。3.4 适度反规范化别为了“规范”把性能搞没了三范式是理想状态但实际开发里你常常要“故意违反”它。反规范化不等于瞎设计而是在性能和一致性之间做取舍。最典型的例子是文章详情页需要显示作者昵称。如果严格按第三范式文章表只存 author_id页面展示时 join 用户表。这没问题但当系统并发量到了几千 QPS每次详情查询都 join 一次压力不小。这时候很多团队直接选择在文章表里冗余一个 author_nickname 字段减少一次 join提升查询性能。还有计数器的设计。文章表里字段 view_count 就是冗余统计值如果完全按范式的思路应该靠 count 评论表里的记录数来计算。但那样每次查询都要做聚合非常慢。冗余一个计数字段写入时加一读的时候直接取性能能提高一个量级。反规范化的代价是数据一致性风险。你冗余了 nickname用户改昵称之后文章表里的旧昵称不会自动更新。这种时候只能通过代码层同步更新或者接受昵称不是实时一致的现实。我的建议是核心数据严谨按三范式查询热点和统计展示可以适度反规范化但你要非常清楚自己牺牲了什么。4. 物理设计与实操落地写出一份能上线的建表 SQL逻辑模型定了表结构也画出来了接下来才轮到真正的建表。这一步像装修的“硬装”字段类型、索引、存储引擎每一样都必须认真对待。4.1 存储引擎和字符集选择以 MySQL 为例绝大多数场景直接选 InnoDB。它支持事务、行级锁、崩溃恢复连 8.0 版本的默认引擎都是它。MyISAM 这种古董引擎到现在还偶尔见到但它不支持事务和外键并发场景下性能和安全都落后没有理由再选。字符集我果断用 utf8mb4。很多老项目还在用 utf8但 utf8 在 MySQL 里实际上是 utf8mb3最多只能存 3 个字节的字符遇到 emoji 就会报错或者存成乱码。utf8mb4 是 utf8 的超集能完整存储 Unicode包括那些花里胡哨的 emoji。现在的 MySQL 8.0 默认就是 utf8mb4如果你还在用 5.7建表时也请显式指定。排序规则随默认就好utf8mb4_general_ci 已经够用不需要太纠结。4.2 字段类型的选取别把日期存成字符串字段类型选择是建表的核心我踩过的坑几乎都能排成队。最常见的坑是把日期存成 varchar 类型。这导致完全没有日期函数的支持比较大小靠字符串比较排序严重依赖格式数据一但格式不统一查询结果就是一副扭曲的画面。正确做法是用 DATETIME 或 TIMESTAMP 存储日期时间用 DATE 存储生日这类纯日期。金额字段也经常出错。用 FLOAT 或 DOUBLE 存钱加减运算会积累浮点误差结果是我明明转出去 0.1 元账上却显示 0.0999999999。金额这类精确数值必须用 DECIMAL。DECIMAL(10,2) 一般够用大额交易可以 DECIMAL(14,2) 或更高。手机号这类字符串我见到的第二个坑是用 INT 或 BIGINT 存。手机号本身不是用来做算术运算的用整数类型纯属自我麻烦。更离谱的是手机号有前导零的情况用 INT 直接丢位。用 VARCHAR(20) 存手机号才是正解。给一张常用的类型选择参考表数据场景推荐类型说明主键、外键BIGINT UNSIGNED大整数无符号状态、枚举TINYINT0/1/2 数字枚举标题、名称、手机号VARCHAR(50/20/20)长度按业务预留正文、长文本TEXT / LONGTEXT不建索引价格、金额DECIMAL(10,2)精确小数日期时间DATETIME时区处理直观创建/更新时间DATETIME 默认值见下方建表4.3 索引设计一开始别贪多常用路径优先索引能加速查询但也是成本和负担。每次插入、更新都要同步维护索引索引文件本身也要吃磁盘和内存。初学者容易犯的毛病是看哪个字段顺眼就加索引结果索引比数据还大查询性能不升反降。我的索引设计原则只有记住一条序列主键索引 → 唯一索引 → 高频查询路径。主键索引数据库自动建不用管。唯一索引只加在有唯一约束的业务字段上手机号、邮箱这类有全局唯一需求的字段才加。普通索引就是针对“WHERE 后面经常出现的过滤条件”和“ORDER BY 高频排序字段”加。还要知道一个概念叫联合索引。当查询条件同时包含 author_id 和 status就要建 (author_id, status) 联合索引而不是分别建两个单列索引。联合索引遵循最左前缀原则最左边的字段必须出现在查询条件里索引才会生效。我实际开发里习惯把等值条件写在联合索引前面范围条件写在后面。但注意索引不是建了就有用。对 LONGTEXT 字段加索引大概率会失败即便能建成查询效率也低得可怜。长文本如果需要查询应该考虑加前缀索引也就是只对前 N 个字符建立索引。4.4 完整建表语句演示从用户信息表到文章表纸上谈兵得够了上一份可以直接运行的参考建表 SQL。以下都以 MySQL 8.0 为例核心思路同样适用于 MySQL 5.7。先建用户信息表对应热词里的“第1关用户信息表”CREATE TABLE user_info ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 用户ID代理主键, mobile VARCHAR(20) NOT NULL COMMENT 登录手机号, nickname VARCHAR(64) NOT NULL COMMENT 用户昵称, password_hash VARCHAR(128) NOT NULL COMMENT 密码哈希值禁止明文, avatar_url VARCHAR(512) DEFAULT NULL COMMENT 头像地址, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1正常 0禁用, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_mobile (mobile) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户信息表;注意点mobile 加了唯一约束防止同手机号二次注册password_hash 用 varchar(128) 存放哈希结果而不是明文密码status 设置了默认值创建用户时即使代码忘了传值数据库也不会乱。再建文章表CREATE TABLE article ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 文章ID, author_id BIGINT UNSIGNED NOT NULL COMMENT 作者ID关联用户表, category_id BIGINT UNSIGNED DEFAULT NULL COMMENT 文章分类ID可空, title VARCHAR(200) NOT NULL COMMENT 文章标题, content LONGTEXT COMMENT 文章正文, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0草稿 1已发布 2已下架, view_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 阅读数冗余计数器, published_at DATETIME DEFAULT NULL COMMENT 发布时间, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_author_status (author_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章表;这里 category_id 是可空的因为文章创建时允许先不选分类发布时再补也不是不行。联合索引 idx_author_status 覆盖了“查询某个作者在某状态下的文章”这个高频场景这类查询在作者后台列表页非常常见。再补一张多对多关联表CREATE TABLE article_tag ( article_id BIGINT UNSIGNED NOT NULL COMMENT 文章ID, tag_id BIGINT UNSIGNED NOT NULL COMMENT 标签ID, PRIMARY KEY (article_id, tag_id), KEY idx_tag (tag_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章标签关联表;联合主键 (article_id, tag_id) 能保证一篇文章不会重复打同一个标签按文章查标签很快按标签反查文章可以在 idx_tag 索引加持下也很快。4.5 插入数据验证设计建完表不要直接跑业务代码先用几条测试数据验证约束和索引是否符合预期。我习惯用几个 INSERT 语句快速压一遍。INSERT INTO user_info (mobile, nickname, password_hash) VALUES (13800138000, 小明, a665a45920422f9d); INSERT INTO article (author_id, category_id, title, content, status) VALUES (1, NULL, 我的第一篇博客, 你好欢迎来到我的博客。, 1); INSERT INTO article_tag (article_id, tag_id) VALUES (1, 1), (1, 2);插入之后再写一条 JOIN 查询看看能不能顺利把文章、作者昵称、标签名查出来。这个验证动作能帮你尽早发现外键引用错误、类型对不上这些基础问题等代码写到你再来排查成本高得多。5. 常见问题与避坑指南这些坑我踩过你别再踩做数据库设计这么多年有些坑是新手必踩有些坑是老手偶尔也会翻车的。盘点一下最常见的几类问题和我的排查思路希望你少走弯路。5.1 设计不足和过度设计怎么掌握“刚刚好”这个问题我刚才埋了个伏笔现在展开讲。设计不足好理解需求只覆盖了今天没想到明天表结构完全撑不住新需求。过度设计则相反啥功能都想预留下来一张用户表把购物车、收货地址、收藏夹统统塞进去最后表又大又乱谁也看不懂。我的经验是用“迭代思维”做设计先定最小闭环但要在设计时留出合理的扩展信号。比如用户表不要轻易把 username 和 mobile 混成一个字段因为登录方式一变就麻烦。相反不要因为想着用户会有“性别、年龄、星座”就提前建一堆可空字段空字段对你的查询没有任何帮助长期维护只是负担。设计时做一个简单假设凡是近期确定要用的字段现在就建确定未来用但暂不清楚形态的等到用的时候再建。5.2 外键到底要不要加触发器能不能滥用我见过一些项目建表时把外键约束拉满删除用户时 Database 层能把相关数据全部联动删干净。听起来很省心但真的生产环境跑起来这些级联操作可能引发锁竞争、死锁甚至在删除一个超大用户时直接把主库拖死。我现在做项目偏向后端代码控制逻辑数据库只保留主键和唯一键外键关系由应用层管理。这种方案灵活度高对性能也友好。触发器我基本不推荐。触发器会把业务逻辑藏在数据库里出了问题排查链路长而且它不是在事务边界之外跑容易把简单逻辑变得很折磨。如果你发现自己在触发器里写复杂计算赶紧把这些逻辑挪到应用层去。5.3 字段类型选错导致的问题我挑几个高频翻车现场再说一遍。用 float 存金额结算时会出现 0.1 0.2 ! 0.3 的幻觉用 varchar 存时间日志查询和排序做起来疯狂想砸键盘用 int 存手机号遇到 13 位数字超int范围直接报错或者前导零被吃掉用 text 当索引字段非但没有提速反而把辅助索引的体积膨胀到吓人。这些问题的排查思路很简单写完建表 SQL 后不要急着执行先按数据业务去验证一遍这个字段会不会拿来参与计算这个字段会不会参与比较大小这个字段最长可能是多少位带着这堆问题再review一遍字段类型绝大多数错都能避免。5.4 NULL 值用得太多查询性能的隐形杀手NULL 不是一个普通的空值它在数据库里是一个特殊的标记索引不会高效处理聚合会跳过它WHERE 条件通常要另外加 IS NULL 判断代码里也容易遇到 NULL 引发空指针。能用默认值的字段尽量设置 NOT NULL DEFAULT xxx千万不要放任字段默认可空。比如文章状态字段如果默认 NULL查询“已发布文章”你要写成 status 1查询“草稿文章”要写成 status 0但如果某篇文章没设置状态它既不在草稿也不在发布整个数据逻辑就变模糊了。把 status 设为 NOT NULL DEFAULT 0每一个语义都明确代码判断也简单。5.5 上线后表结构要改别在线上直接 ALTER TABLE人算不如天算上线后总是会有新的需求要加字段。这里最忌讳的是一边登录线上环境一边手敲 ALTER TABLE 给正在跑业务的表塞列。我经历过一次凌晨三点加字段表已经有 5000 万行数据ALTER 跑了 40 分钟期间写入全部阻塞用户侧的报错铺天盖地。正确做法是把表结构变更管起来。所有结构修改先走变更脚本提交到 Git 仓库经过 review 之后通过工具执行。小团队可以不用复杂工具但至少要保证每一次 DDL 都有记录能回滚能追溯。你还得知道大表加字段需要评估锁表时间MySQL 8.0 对 INSTANT ADD COLUMN 做了优化但也不是所有字段类型都支持零成本加列所以线上操作要谨慎为上。最后给你一个非常实用的习惯每次建表都把建表 SQL 导出来存到项目目录里的 sql/ 文件夹带上日期和版本号。几个月后你想知道当时为什么这么设计打开文件看注释就懂了。一个项目跑上两三年你回看这些SQL文件会感谢当时那个认真设计的自己。数据库设计这条路真的没有一个统一答案但方法论是相通的先业务再概念再逻辑最后物理实现每一步都踩稳后面才不会返工。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑