ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Prisma 数据导入导出实战:基于 NDF 格式完成服务备份、迁移与跨 Stage 数据复制

Prisma 数据导入导出实战:基于 NDF 格式完成服务备份、迁移与跨 Stage 数据复制 Prisma 数据导入导出实战基于 NDF 格式完成服务备份、迁移与跨 Stage 数据复制【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址: https://gitcode.com/gh_mirrors/pr/prisma1本指南以 Prisma本仓库对应 prisma1 时代的 CLI 与服务器实现的官方数据库工作流文档为主线完整讲解如何借助Normalized Data FormatNDF规范化数据格式实现数据的导出与导入。你将掌握prisma init初始化服务、seed种子数据、prisma export导出、部署到新stage、prisma import导入的完整闭环并结合本仓库 CLI 源码prisma-cli-core理解底层游标分页下载 分块上传的实现原理从而在真实项目中安全地完成数据备份、环境复制与迁移。NDFPrisma 导入导出的中间格式Prisma 在导入与导出数据时统一使用一种专门设计的中间格式——Normalized Data FormatNDF详见官方参考文档。NDF 本质上是对 JSON 结构的一种约定它把一条数据模型里的信息拆成三种value type值类型分别存放值类型职责对应场景nodes存放节点的标量字段数据相当于数据库记录本身单条记录的普通字段lists存放节点的标量列表字段数据[String!]!这类标量列表relations存放通过关系字段连接两个节点的数据一对多、自关联等关系一份 NDF JSON 文档的顶层结构永远是包含两个键的对象valueType取值只能是nodes、lists或relations和values按值类型组织的数据数组。下面的示例均基于如下数据模型type User { id: String! unique firstName: String! lastName: String! hobbies: [String!]! partner: User }nodes标量字段数据valueType为nodes时values数组中的每个对象结构为{ valueType: nodes, values: [ { _typeName: STRING, id: STRING, scalarField1: ANY, scalarField2: ANY, ..., scalarFieldN: ANY }, ... ] }其中_typeName与id必须是字符串_typeName指数据模型中 SDL 类型的名称scalarFieldX是该类型的各个标量字段名。例如导入两个User节点的标量数据{ valueType: nodes, values: [ {_typeName: User, id: johndoe, firstName: John, lastName: Doe}, {_typeName: User, id: sarahdoe, firstName: Sarah, lastName: Doe} ] }lists标量列表字段数据valueType为lists时结构为{ valueType: lists, values: [ { _typeName: STRING, id: STRING, scalarListField: [ANY] }, ... ] }与nodes不同每个对象一次只能为某一个列表字段提供值。例如写入两个User的hobbies列表{ valueType: lists, values: [ {_typeName: User, id: johndoe, hobbies: [Fishing, Cooking]}, {_typeName: User, id: sarahdoe, hobbies: [Biking, Coding]} ] }relations关系连接数据valueType为relations时values数组里的每个元素本身是一个恰好包含两个元素的数组pair分别描述关系两端的节点{ valueType: relations, values: [ [ { _typeName: STRING, id: STRING, fieldName: STRING }, { _typeName: STRING, id: STRING, fieldName: STRING } ], ... ] }_typeName、id、fieldName均为字符串其中fieldName是关系字段名。例如通过partner关系字段把两个User节点连接起来{ valueType: relations, values: [ [ { _typeName: User, id: johndoe, fieldName: partner }, { _typeName: User, id: sarahdoe, fieldName: partner } ] ] }导入导出的整体流程官方文档用如下示意图概括了数据进入 Prisma 的完整管线-------------- ---------------- ------------ | | | | | | | SQL | (1) transform | NDF | (2) chunked upload | Prisma | | MongoDB | --------------- | | ------------------- | | | JSON | | | | | | | | | | | -------------- ---------------- ------------第 (1) 步transform把 MySQL、MongoDB、JSON 等具体数据源转换为 NDF。官方明确指出这一步在 1.11 时代需要手动完成虽然文档提到未来 CLI 将支持直接从这些数据源导入对应 graphcool/framework 仓库的 issue #1410但尚未可用。第 (2) 步chunked upload分块上传把 NDF 数据上传给 Prisma 服务既可以通过 CLI 的prisma import命令也可以通过服务端暴露的原生/importHTTP API。整个教程将按照如下步骤展开创建一个 Prisma 服务为服务植入seed初始数据以 NDF 格式导出数据把服务部署到一个新的 stage以 NDF 格式导入数据第一步创建 Prisma 服务在终端中进入任意目录并执行prisma init import-example交互提示选择模板时选择Minimal setup: database-only即可。这会创建一个名为import-example的新目录其中包含根配置文件prisma.yml与数据模型定义文件datamodel.graphql。接下来更新数据模型加入一个关系。把datamodel.graphql的内容改为type User { id: ID! unique name: String! posts: [Post!]! } type Post { id: ID! unique title: String! author: User! }这里User.posts与Post.author构成一对多关系——稍后导出 NDF 时relations类型的数据就负责持久化这种关联。第二步Seed 初始数据在import-example目录内新建文件seed.graphql写入如下 mutationmutation { createUser(data: { name: Sarah, posts: { create: [ { title: GraphQL is awesome }, { title: It really is }, { title: How to GraphQL is the best GraphQL tutorial } ] } }) { id } }接着在prisma.yml中声明seed属性告诉 CLI 这个种子文件的存在service: import-example stage: dev datamodel: datamodel.graphql # to enable auth, provide # secret: my-secret disableAuth: true seed: import: seed.graphql部署服务时CLI 会把seed.graphql中的 mutation 发送到服务的 API 执行prisma deploy交互提示选择部署目标cluster时选择公共集群prisma-eu1或prisma-us1之一官方文档特别说明使用 Docker 本地部署时 seed 同样可用。部署完成后在 GraphQL Playground 中发送如下查询验证 seed 是否生效{ users { name posts { title } } }Prisma API 应返回{ data: { users: [ { name: Sarah, posts: [ { title: GraphQL is awesome }, { title: It really is }, { title: How to GraphQL is the best GraphQL tutorial } ] } ] } }第三步以 NDF 导出数据在import-example目录执行prisma export该命令会在当前目录生成一个名为export-__TIMESTAMP__.zip的压缩包其中__TIMESTAMP__是导出的精确时间点。从源码看默认文件名由 export/index.ts 中的export-${new Date().toISOString()}.zip生成若传入的路径不以.zip结尾还会自动补上后缀。zip 内的文件即 NDF 格式数据。导出命令的完整参数prisma export的完整用法与参数见命令参考文档prisma export [flags]-e, --env-file ENV-FILE Path to .env file to inject env vars -p, --path PATH Path to export .zip file两个常用示例# 使用默认文件名 export-timestamp.zip prisma export # 导出到指定文件 prisma export --path mydata.zip底层实现游标分页下载导出过程的底层逻辑值得展开。在 Exporter.ts 中CLI 依次下载三类数据先nodes、再lists、最后relations每类数据都在工作目录下创建.export/类型/文件夹随后用archiver打包成 zip 并清理临时目录。关键点在于游标分页见downloadFiles方法prisma export不是一次性取回全部数据而是维护一个包含table、row、field、array四个偏移量的cursor反复请求导出 API每轮拿到数据后把返回的新cursor作为下一轮请求的入参直到cursor四个值之和小于 0即全部为-1表示导出终止。每个分页文件按000001.json这类 6 位前导零格式递增命名leadingZero函数并统一包装成{ valueType: fileType, values: ... }的 NDF 文档。这正是文档中CLI 自动处理 cursor、你无需手动发多个请求调整游标这一优势的源码体现。第四步部署到新的 stage接下来通过部署到新 stage 来创建服务的克隆。打开prisma.yml把stage改为新值并移除seed与cluster属性service: import-example stage: test datamodel: datamodel.graphql # to enable auth, provide # secret: my-secret disableAuth: true再次执行prisma deploy把服务部署到新的teststage和之前一样部署目标选择prisma-eu1或prisma-us1。此时新 stage 是空库正好用来承接下一步的导入。第五步以 NDF 导入数据服务运行起来后即可把刚才导出的 zip 导入新 stage。执行如下命令需将__DATA__占位符替换为导出 zip 的路径例如export-2018-01-13T19:28:25.921Z.zipprisma import --data __DATA__导入命令的完整参数prisma import的用法见命令参考文档prisma import [flags]-d, --data PATH (required) Path to zip or directory with import data (NDF) -e, --env-file ENV-FILE Path to .env file to inject env vars其中--data短名-d为必填指向包含待导入数据的目录路径既可以是普通目录也可以是 zip 压缩目录。源码 import/index.ts 会先校验该路径存在并强制要求它是目录或以.zip结尾否则直接报错。导入完成后为当前teststage 打开 GraphQL Playground再次发送上面查询users的语句即可确认数据已被成功复制。CLI 导入的目录约定使用 CLI 导入时NDF 数据文件需要放在以类型命名的三个子目录nodes、lists、relations中。每个 NDF 文件必须是.json且在同一目录内需从 1 开始递增编号允许前导零例如1.json、01.json或0000001.json。一个典型的导入目录结构如下示例见数据导入参考文档. ├── data │ ├── lists │ │ ├── 0001.json │ │ ├── 0002.json │ │ └── 0003.json │ ├── nodes │ │ ├── 0001.json │ │ └── 0002.json │ └── relations │ └── 0001.json ├── datamodel.graphql └── prisma.yml随后执行prisma import --data data底层实现校验、分块上传与断点续传从 Importer.ts 可以看到导入的完整内部流程若传入的是 zip先解压到.import/临时目录用globby收集nodes、lists、relations三个目录下的所有.json文件交由 Validator.ts 做基本校验依据valueType分别检查节点、关系二元组、列表字段的合法性按nodes → lists → relations的顺序逐个文件调用上传 API 分块上传每成功上传一个文件就把进度写入.import/state.json若中途失败下次重跑时会读取state.json跳过已导入的文件源码中以Skipping file ... (already imported)日志体现——这就是官方文档所说 CLI 支持暂停与恢复的机制全部完成后重置 state 并清理临时目录。CLI 方式的优势因此非常明确一次上传多个文件、复用 CLI 的认证机制无需手动携带 token、支持暂停/恢复、并预留了未来直接从 MySQL/MongoDB/Firebase 等数据源导入的能力。进阶使用原生 HTTP API 导入导出除 CLI 外Prisma 服务还在 HTTP 端点下暴露了原生导入导出 API详见数据导入参考与数据导出参考导入服务端点/import例如http://localhost:60000/my-app/dev/import或https://database.prisma.sh/my-app/prod/import导出服务端点/export例如http://localhost:60000/my-app/dev/export或https://database.prisma.sh/my-app/prod/export单次请求最多传输10 MB的 NDF JSON 数据且必须在 HTTPAuthorization请求头中携带认证 token。导入示例curl上传一份nodes类型的 NDF 数据curl http://localhost:60000/my-app/dev/import \ -H Content-Type: application/json \ -H Authorization: Bearer __JWT_AUTH_TOKEN__ \ -d {valueType:nodes,values:[{_typeName:Model0,id:0,a:test,b:0,createdAt:2017-11-29 14:35:13},{_typeName:Model1,id:1,a:test,b:1}]} \ -sSv导出请求则需要在 body 中携带fileType与cursor{ fileType: nodes, cursor: { table: 0, row: 0, field: 0, array: 0 } }cursor中的四个值表示从数据库中何处开始导出的偏移量每次响应都会返回新的cursor其状态有两种终止态未满table、row、field、array全部为-1表示导出已完成非终止态已满任一值不为-1表示本次响应已达 10 MB 上限需把返回的cursor作为下一次请求的输入继续导出。对应的curl示例curl http://localhost:60000/my-app/dev/export \ -H Content-Type: application/json \ -H Authorization: Bearer __JWT_AUTH_TOKEN__ \ -d {fileType:nodes,cursor:{table:0,row:0,field:0,array:0}} \ -sSv注意原生 API不做任何数据校验导入非法数据可能导致未定义行为甚至破坏服务CLI 导入则执行基础校验。作为服务维护者你需要自行确保导入数据的有效性。关键注意事项与限制导入不是幂等的prisma import只会向服务追加数据永远不会更新已有节点。同一数据集导入多次、或导入具有相同id的节点都会导致未定义行为并可能损坏服务详见数据导入参考文档中的警告。导出数据可直接回灌同构 schema 的服务因为导出结果就是 NDF直接导入到 schema 完全一致的服务即可例如为devstage 制造测试数据见数据导出参考文档。文件体积建议CLI 导入时每个 NDF 文件建议不超过 1 MB否则可能触发超时文件数量本身不限。文档存储MongoDB暂不支持从 export/index.ts 与 import/index.ts 的源码可以看出当prisma.yml中databaseType为document时两个命令都会直接抛错提示改用 MongoDB 原生的mongodump/mongorestore。本仓库对应 prisma1 版本还会打印该命令未来不再继续开发、请改用数据库原生导入导出功能的弃用警告分别指向 MySQL 的mysqlimport与 Postgres 的pg_restore。数据校验责任在维护者官方建议在导入前先用相同数据模型的既有服务做一次prisma export以导出文件为参照核对数据有效性这是确保导入数据合法的最稳妥方式。小结通过本文的完整演练你可以在 Prisma 服务之间安全地复制、备份和迁移数据用prisma init初始化服务、用seed属性配合seed.graphql植入初始数据、用prisma export生成 NDF 格式的 zip 备份、通过修改stage重新部署得到克隆服务再用prisma import --data把备份灌入新 stage。与此同时理解 NDF 的nodes/lists/relations三种值类型、prisma export的游标分页下载与prisma import的校验 分块上传 state.json 断点续传机制能帮助你在需要绕过 CLI、直接对接/import与/export原生 API 时依然写出正确、可恢复的自动化数据管道。【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址: https://gitcode.com/gh_mirrors/pr/prisma1创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表