四个症状,四个名字
这个连载一直以手机电子请柬服务为例阅读 ERD。ERD 是把数据库的表以及表与表之间的关系画在一张纸上的设计图。01 篇讲了实体(管理对象)和列,02 篇讲了一对多关系,03 篇讲了多对多关系和连接(join)。
讲座最后只简单介绍了四个进阶主题:N+1 问题、事务、索引、迁移。光听名字像是开发者的领域,但讲座的讲法不是代码,而是症状:出现什么情况,就怀疑什么。
这种讲法正适合氛围编程者。把代码交给 AI 的人,能直接看到的是画面和行为。代码里的原因找不到,但「列表很慢」「积分没到账」是能察觉的。能给症状叫出名字,就知道该让 AI 检查什么。
下表就是全文的摘要。遇到左列的症状,就把中间列的名字放进去,像右列那样请 AI 检查。
| 症状 | 怀疑什么 | 对 AI 的请求 |
|---|---|---|
| 列表页面太慢 | N+1 问题、索引 | 检查这个列表有没有 N+1 问题、有没有缺索引,两个都查 |
| 搜索太慢 | 索引 | 检查这个搜索用到的列有没有设置索引 |
| 注册成功了,300 积分却没到账 | 事务 | 检查注册和发放积分是否包在同一个事务里 |
| 要搬动已有数据,比如把一列改成单独的表 | 迁移 | 执行之前先给我看分步计划 |
列表很慢 — N+1 问题,以及索引

想象一下请柬列表页面。每张请柬旁边都显示它用的模板,带缩略图和模板地址(URL)。可请柬表里没有模板信息,只有模板的 id(template_id)。像 03 篇讲的那样,到模板表里找到这个 id 那一行,把地址拉过来贴上。这就是连接。
列表页面通过 API 拿到这些数据。API 是页面用来要数据的窗口。写得好的列表 API 会把请柬和模板连接起来一次取回。N+1 问题就是不做连接:先取一次请柬列表,然后每一行再单独查一次模板。列表查询一次(1),加上行数那么多次查询(N),所以叫 N+1。
请柬只有几张时看不出差别,行越多,查询次数跟着越多。所以讲座说要按症状记:调用列表 API 慢得离谱时,就怀疑它。这时对 AI 说「检查这个列表 API 有没有 N+1 问题」就行。
索引的症状也类似。索引就是事先为数据库做好的目录。在没有目录的书里找某一段,只能从头翻;用没有索引的列去搜索,就得把整张表扫一遍。搜索变得太慢,就怀疑索引。
讲座强调的是两个一起怀疑。调用列表之类的 API 慢得离谱时,要把两件事都问一遍:是 N+1 问题,还是索引没设好?症状一样,只查一边就可能漏掉原因。
显示 20 张请柬的列表时
N+1(慢的做法)
① 查询 20 张请柬 → 1 次
② 每张请柬单独查询模板 → 20 次
合计 21 次,请柬越多越多
连接(快的做法)
① 用 template_id 把请柬和模板连起来一次查询 → 1 次注册成功了,积分却没有 — 事务
讲座的例子是会员注册。假设有给新注册用户发 300 积分的功能。数据库里用户表和积分表是分开的。注册一次,两张表都要写进记录。
亲自注册一下,可能会出现用户建好了、300 积分却没加上的情况。本该一起发生的事,因为表是分开的,只做成一半。留下一个没有积分的半成品账号。
事务就是把多个动作当作一个整体来处理。捆在一起的动作要么一起成功,要么一起失败。就像银行转账,从我账户扣钱和对方账户进钱这两件事不能各走各的。发放积分失败,注册也要当作没发生过。
症状是「本该一起发生的事只发生了一件」。检查方法和讲座的例子一样:自己注册一次,看积分到没到。没到,就对 AI 说「检查会员注册和发放 300 积分是否包在同一个事务里。只要有一个失败,两个都要取消」。
| 注册 | 发放积分 | 没有事务时 | 有事务时 |
|---|---|---|---|
| 成功 | 成功 | 正常 | 正常 |
| 成功 | 失败 | 只留下没有积分的用户 | 两个都取消 |
把模板搬进表里时 — 迁移

迁移就是新增或修改数据库里的实体。按讲座的说法,新加一张表这种程度问题不大。要小心的是必须搬动已存数据的变更。
典型例子是 02 篇里模板的升级。起初,请柬表的模板列规定只能填 1、2、3 之一。这种只允许预先定好的值的方式叫 enum。等到模板需要管理时,就把模板拆成了单独的表。在设计图上只是改一条线,但如果已经有做好的请柬,情况就不同了。
顺序是这样的。先建模板表,把 1、2、3 放进去,每一行都会有指向该行的 id。接着把已有请柬的模板值 1、2、3 一一对应到新的 id,填进 template_id。两张表已经建立了关系,对应关系必须对上才不会报错。最后删掉不再需要的旧模板列。
讲座的警告是:这种大规模变更交给 AI 却什么都不看,事故会非常多。据讲座说,「数据库操作失误,数据全没了」这类事,大多是没留意迁移、全交给 AI 的情况。所以它建议养成习惯:这类工作不要只交给 AI,每次都亲自确认一遍。
确认不一定要读代码。执行前先要分步计划,确认搬动前后请柬数量一致、每张请柬都填好了 template_id,之后再让它删除旧列。因为删除这一步最难撤回。
之前:invitations
id title template
1 请柬 A 1
2 请柬 B 3
① 建 templates 表并放入 1、2、3
id url thumbnail
t-01 ... ...
t-02 ... ...
t-03 ... ...
② 填写 invitations.template_id
template 1 → templates.id t-01
template 3 → templates.id t-03
③ 删除旧的 invitations.template 列 ← 最难撤回我想做一次迁移,把模板列搬到 templates 表里。
执行之前,先给我看分步计划。
也告诉我怎么确认搬动前后请柬数量一致、
每张请柬都填好了 template_id。
旧的 template 列等我确认之后再删。完成的 ERD 让你看到什么
讲者认为最重要的不是这四种事故,而是完成的 ERD 本身。看着完成的 ERD,做了什么、没做什么一目了然。
需要开发新功能时,能看出该在哪里加什么;根据现有的关联关系,也能自己很快判断哪些功能能做、哪些做不了。01 篇说的、几个月后还原「我到底做了什么」的那张地图,就是它。
四种事故在这张地图上也各有位置。N+1 问题是沿着表与表之间的连线取数据的方式出了问题;索引是贴在表里常被查找的列上的目录。事务是一个动作牵动的两张表之间的问题;迁移是改线那一刻的问题。能读懂 ERD,出现症状时也就知道该让 AI 看哪里。
基于 ERD 讲座的四篇到此结束。下一篇起转到 HTML 讲座,以咨询申请表单为例,看看为什么 UI 是有正确答案的问题。
