引言
当你第一次使用 Prisma 的 migrate
命令时,感觉就像拥有了魔法。你只是在 schema.prisma
文件里改了几行定义,终端里敲下一行命令,数据库的表结构就自动、精准地更新了。它甚至还贴心地在 prisma/migrations
目录下为你创建了一个带时间戳的 SQL 文件,记录了这次变更。
一切都如此丝滑。
然后,一个很自然的想法就会冒出来:随着项目迭代,migrations
目录下的文件越来越多,几十个、上百个……我能不能在项目稳定后,把这些旧的历史文件删掉,用当前的 schema.prisma
生成一个全新的、干净的“初始迁移”文件呢?
答案是:绝对,绝对不能!
如果你这么做了,就等于亲手埋下了一颗未来随时可能引爆的定时炸弹。要理解原因,我们必须揭开 Prisma 迁移的“黑魔法”,看看那些历史文件到底扮演了多么重要的角色。
为什么?
误区:schema.prisma
就是一切?
很多人认为,schema.prisma
文件是迁移的“唯一真理之源”。Prisma 不就是靠它来生成 SQL 的吗?
这个理解只对了一半。更准确地描述是:
schema.prisma
是你的【最终设计蓝图】。它描述了你希望你的数据库 最终 变成什么样子。prisma/migrations
目录是你的【施工日志】。它记录了从一片空地到建成这座宏伟大厦的 每一个精确步骤。
你不能拿着最终蓝图,烧掉施工日志,然后期望一个新的施工队能分毫不差地重建出一座功能(数据)和结构都完全相同的大楼。
关键场景:当迁移不只是“结构变更”
数据库的演进,远比加个字段、删个表要复杂。最棘手的情况是 包含数据转换的迁移。
让我们看一个经典的例子: 最初,你的用户表有两个字段:firstName
和 lastName
。后来,为了统一,产品决定将它们合并成一个 fullName
字段。
一个正确的迁移过程是三步走:
- 第一次迁移(创建新字段):你在
schema.prisma
里加上fullName
字段,然后运行prisma migrate dev
。这会生成一个 SQL 文件,在数据库中ADD COLUMN "fullName"
。 - 数据转换(最关键的一步):你运行一个脚本,或者直接在生成的 SQL 迁移文件中手动添加一行
UPDATE "User" SET "fullName" = "firstName" || ' ' || "lastName";
。这个指令,将历史数据正确地迁移到了新字段里。 - 第二次迁移(删除旧字段):你从
schema.prisma
中删除firstName
和lastName
,再次运行迁移命令,生成一个DROP COLUMN
的 SQL 文件。
这套组合拳下来,你的数据库不仅结构对了,数据也完美无缺。
现在,想象一下,如果你删除了这些历史迁移文件,然后基于只有 fullName
的 schema.prisma
创建一个“压扁”后的新迁移。会发生什么?
那个至关重要的 UPDATE
数据转换指令,它消失了,永远地消失了。
灾难会如何发生?答案就在于代码和数据库状态的冲突。
- 对于新环境:一个新同事拉取你的代码,他可以创建出正确的表结构,但他的数据库里完全没有那个“合并姓名”的数据逻辑。
- 对于老环境:当你尝试在充满历史数据的生产库上应用这个“全新的”迁移时,会发生如下致命冲突:
- Prisma 的迁移引擎首先连接到你的生产数据库,查看那张名为
_prisma_migrations
的历史表。 - 它在这张表里看到了一条完整的演进记录:
v1
、v2
...v10
、v11
等等。这是数据库自己记载的“施工日志”。 - 然后,Prisma 查看你的项目代码,发现
prisma/migrations
目录里却只有一个文件:V1_baseline...
。 - 冲突爆发! 数据库里的历史记录和你代码里的历史记录完全不匹配。Prisma 不知道该相信谁,为了防止数据损坏或丢失,它会立刻中止操作,并抛出“迁移历史不一致”的严重错误。
- Prisma 的迁移引擎首先连接到你的生产数据库,查看那张名为
历史文件的神圣职责 (与历史表的共舞)
所以,prisma/migrations
目录里的每一个文件都是神圣的,它们与数据库里的 _prisma_migrations
表一起,构成了你数据库健康运作的保障。
- 代码 (
prisma/migrations
目录):定义了数据库应该如何一步步演进的标准蓝图。 - 数据库 (
_prisma_migrations
表):记录了当前这个数据库实例实际上已经走到了哪一步。
两者必须能够步步对应,才能保证迁移的可靠性和一致性。
它们共同保证了:
- 可重复性:任何时候,在任何一个空数据库上,只要按顺序执行完所有迁移文件,就能 100%复现出当前生产环境的数据库,并且
_prisma_migrations
表也会被相应地填满。 - 一致性:保证了团队里每个成员、每个 CI/CD 环境、每个服务器的数据库结构和演进路径都是完全一致的。
- 完整性:不仅记录了结构(DDL),还承载了可能存在的数据转换逻辑(DML),保证了数据的延续和正确。
如何看待日益增多的文件?
“可是文件真的会变得很多啊!” 没错,这是一个现实问题。
但首先,这些纯文本文件对 Git 仓库的体积影响微乎其微。其次,社区和 Prisma 团队也在积极探索“迁移压扁”(Migration Squashing)的官方支持。但在那一天到来之前,最安全、最专业的做法就是:
把 prisma/migrations
目录当作你项目源代码的核心资产,完整地、按顺序地将它提交到版本控制中。
下次当你再看到那个不断增长的目录时,别再嫌它冗长了。你应该感到安心,因为那是你项目稳定性的坚实基石,是你能在数据库层面高枕无忧的根本保证。