跳到主要内容

Schema 增量迁移 ​

概述 ​

Schema 增量迁移是把一个 Graph Space 从当前结构演进到新结构的过程。它不仅包括创建新的节点类型、边类型和属性,还包括历史数据回填、索引就绪、应用兼容、风险审批和发布验证。

图谱通常会长期运行并持续导入数据。例如,一个小说知识图谱最初只有人物和地点,后续可能增加事件、组织、人物别名、关系置信度和来源信息。直接在生产数据库中手工修改结构,会造成应用声明、数据库实际结构和历史数据彼此不一致。

Graph SDK 提供 Schema 比较、差异结果、迁移计划和增量应用能力,帮助上层系统回答:

  • 期望 Schema 与数据库当前结构有什么不同?
  • 哪些变化只是新增,哪些需要审核,哪些可能破坏数据?
  • 当前后端能否表达这些变化?
  • 应用变更后,还需要哪些数据回填和验证?

Graph SDK 不会替业务自动批准或执行所有破坏性 DDL。迁移计划主要用于预览、风险分类和流程编排;备份、审批、灰度、数据回填和回滚仍由上层迁移系统负责。

为什么不能只在启动时重复应用 Schema ​

在开发环境中,应用启动时使用 ADDITIVE 创建缺失结构很方便;但在生产环境中,Schema 变化可能影响:

  • 已经存在的节点和边;
  • 正在运行的查询和导入任务;
  • 索引构建期间的资源和性能;
  • 不同版本应用的兼容性;
  • 知识抽取模型和导入映射;
  • 依赖旧属性、类型或边方向的业务逻辑。

例如,把 Person.age 从字符串改为整数,不只是一次 DDL:历史值可能包含“未知”“约 30 岁”等内容,新旧应用可能同时读写不同类型,索引也可能需要重建。这类变化必须作为数据迁移处理,而不是交给 ADDITIVE 自动覆盖。

迁移中的三种状态 ​

理解迁移需要区分三个 Schema:

状态含义来源
期望 Schema新版本应用希望使用的结构代码仓库、配置中心或 Schema Registry
实际 Schema数据库当前可观察到的结构inspectSchema 反查结果
已发布版本上层记录的最后一次成功 Schema 版本发布记录或迁移数据库

实际 Schema 可能因为手工 DDL、失败迁移或反查能力限制而与已发布版本不同。因此,迁移不能只比较版本号,也不能只相信数据库反查;应同时保留期望制品、发布历史、反查结果和警告。

什么属于增量变化 ​

通常风险较低的新增 ​

  • 新增节点类型;
  • 新增边类型;
  • 给现有节点或边增加可选属性;
  • 新增普通索引;
  • 增加只供开发工具使用的展示元数据。

即使是新增,也需要评估索引构建成本、Schema 传播时间和新旧应用兼容性。将新属性直接设为业务必填,而历史数据没有值,就不能只按低风险新增处理。

需要审核的结构变化 ​

  • 修改属性类型;
  • 把可选属性改为必填;
  • 修改边的期望起点或终点类型;
  • 改变索引字段、目标类型或唯一性;
  • 重命名节点类型、边类型或属性;
  • 改变边方向或业务语义。

“重命名”在数据库中通常不是简单改名,而是新增结构、迁移数据、切换应用、删除旧结构的组合操作。

可能破坏数据的删除 ​

  • 删除节点类型或边类型;
  • 删除仍有数据的属性;
  • 删除索引或唯一约束;
  • 删除旧 Space;
  • 清理旧类型数据和历史来源。

删除会使旧查询、旧版本应用或恢复任务失效,必须经过影响分析、备份和明确审批。

差异比较 ​

先读取数据库当前可观察结构,再以“期望 Schema”为目标进行比较:

java
GraphSchemaInspection inspection =
    graph.manager().inspectSchema("company_knowledge");

GraphSchemaDiff diff = GraphSchemaComparator.compare(
    expectedSchema,
    inspection.getSchema());

差异分为:

  • additions:期望 Schema 有、实际 Schema 没有的节点、边、属性或索引;
  • changes:名称相同但类型、必填、端点或索引定义不同;
  • removals:实际 Schema 有、期望 Schema 没有的定义。

比较器只做结构比较,不读取数据库、不执行 DDL,也不判断数据量、历史值能否转换或查询是否仍然兼容。

反查不完整时的差异风险 ​

如果 inspection.isComplete() 为 false,差异结果可能受到后端元数据缺失影响。例如 Nebula 当前反查不包含完整索引元数据,Neo4j 无法可靠恢复关系端点标签。此时不能把“反查中缺失”直接认定为“数据库中不存在”,更不能据此自动删除或重建。

建议:

  1. 展示全部 inspection warnings 和 unsupported metadata;
  2. 对无法反查的部分参考上次成功发布记录;
  3. 使用后端原生管理命令进行补充核验;
  4. 禁止基于不完整结果自动执行删除。

生成迁移计划 ​

迁移计划将结构差异转换为有顺序的步骤和整体风险等级:

java
GraphSchemaMigrationPlan plan = GraphSchemaMigrationPlanner.plan(
    expectedSchema,
    inspection.getSchema());

if (plan.requiresApproval()) {
    // 提交上层审批流程,不直接执行。
}

风险等级如下:

风险条件建议处理
NONE没有结构差异记录检查结果,无需变更
ADDITIVE只包含新增定义评估容量后可进入兼容发布流程
REVIEW_REQUIRED包含定义变化必须分析数据转换和应用兼容性
DESTRUCTIVE包含删除项需要高权限审批、备份和回滚方案

计划中的步骤是面向审批和控制面展示的结构化描述,不是一个会自动执行任意 ALTER/DROP 的迁移引擎。当前 applySchemaResult(..., ADDITIVE) 只用于后端能够安全表达的增量应用。

推荐迁移流程 ​

1. 准备版本化的期望 Schema ​

每次发布都应有稳定 Schema ID 和版本,例如 customer_graph:2.3。版本制品应与应用代码、抽取规则和导入映射一起评审。

2. 检查目标 Space 和后端能力 ​

确认目标 Space、数据库版本、Edition、账号权限和能力矩阵。不要在错误租户或错误环境中执行迁移。

3. 反查实际 Schema ​

读取当前结构,保存反查时间、后端信息、完整性、警告和不可映射元数据。反查结果应成为本次迁移审计材料的一部分。

4. 生成差异和迁移计划 ​

分别展示新增、变化和删除,不能只给出一个“有差异”的布尔值。对于每项变化,应附带受影响的查询、写入、导入和数据规模评估。

5. 数据预检查 ​

在执行 DDL 前验证:

  • 历史属性是否能转换为新类型;
  • 将属性设为必填时是否存在空值;
  • 唯一约束是否存在重复数据;
  • 删除类型或属性时是否仍有数据和调用方;
  • 新索引预计构建时间和资源是否可接受。

6. 审批和安排发布窗口 ​

REVIEW_REQUIRED 和 DESTRUCTIVE 必须由上层审批。高成本索引、海量回填和写入不兼容变化也应进入维护窗口,即使结构上看起来只是新增。

7. 执行兼容变更 ​

优先创建新类型、新属性和新索引,让新旧应用都能运行。使用结构化结果记录已执行步骤、警告、错误码和耗时:

java
GraphSchemaApplyResult result = graph.manager().applySchemaResult(
    "company_knowledge",
    expectedSchema,
    GraphManager.SchemaMode.ADDITIVE);

8. 等待后端真正就绪 ​

Nebula 的 Tag、Edge、属性和索引存在异步传播;索引创建后还需要重建。Neo4j 索引也可能经历构建过程。DDL 返回成功不应直接等同于查询已使用新结构。

9. 回填和转换历史数据 ​

新增属性、类型替换或模型重构通常需要独立数据任务。回填应具备:

  • 可重复执行的批次;
  • checkpoint 和失败记录;
  • 新旧字段一致性检查;
  • 限速和数据库容量保护;
  • 明确的撤销或重跑方案。

10. 切换应用并验证 ​

执行代表性写入、查询、分页、聚合和 Explain,确认新版本应用与真实后端共同工作。不要只验证 Schema 列表中出现了新字段。

11. 清理旧结构 ​

旧属性、旧边和旧索引应经过观察期后再清理。清理属于单独的破坏性发布,不应与新增结构在同一步自动完成。

12. 保存发布记录 ​

至少记录:Space、后端、Schema ID/版本、差异、计划、审批人、执行人、开始结束时间、结果、警告、回填任务和验证结论。

Expand and Contract 模式 ​

对生产系统,推荐使用“扩展后收缩”模式完成不兼容变化。

以 Person.fullName 替换 Person.name 为例:

Expand:扩展 ​

  1. 新增 fullName,保留 name;
  2. 新应用同时兼容两个字段;
  3. 新写入开始写 fullName,必要时双写;
  4. 回填历史节点;
  5. 建立并验证新索引。

Migrate:迁移 ​

  1. 检查所有历史节点已回填;
  2. 切换查询和抽取规则使用 fullName;
  3. 观察旧字段读取量和错误率;
  4. 停止对 name 的写入。

Contract:收缩 ​

  1. 确认没有旧应用和恢复任务依赖 name;
  2. 备份或导出必要数据;
  3. 单独审批删除旧索引和旧属性;
  4. 更新期望 Schema 和发布记录。

这种方式比原地修改类型或字段名更容易灰度、回滚和兼容多版本应用。

常见迁移场景 ​

新增可选属性 ​

通常属于 ADDITIVE。应用 Schema 后,应等待属性传播并验证新写入;如果查询依赖该属性,还需要建立适当索引。

新增必填属性 ​

结构上是新增,但业务上不是低风险操作。应先按可选属性发布,完成历史数据回填和新写入适配,再提升应用校验要求。不要假设后端会自动为历史节点补值。

修改属性类型 ​

不要依赖 ADDITIVE 原地修改。新增目标类型属性,转换和回填数据,切换读写,最后清理旧属性。需要处理无法转换的历史值。

重命名节点或边类型 ​

通常需要创建新类型、复制或重写数据、重建关系与索引、切换查询,再删除旧类型。大图上的复制成本可能很高,应评估是否真的需要物理重命名。

修改边端点或方向 ​

这会改变图的查询语义。应作为数据重构处理,创建新边、校验数量和方向、切换查询,最后删除旧边。

新增索引 ​

先评估索引大小和构建资源,再创建并等待可用。通过 Explain 或真实查询验证索引是否被使用。Nebula 还需要关注索引重建和传播。

删除索引 ​

删除前确认没有关键查询依赖它,并准备性能回退方案。删除后应运行代表性查询和性能基线对比。

Neo4j 与 Nebula 的迁移差异 ​

Neo4j ​

  • 属性模型较动态,Portable Schema 主要管理稳定 ID 约束和索引;
  • 新属性可能随数据写入自然出现,但这不等于完成了数据回填;
  • 节点唯一约束可用,关系唯一约束不属于当前 Portable 能力;
  • 关系端点标签无法由 Portable Schema 完整强制和反查;
  • 大型索引和约束仍需在真实版本中评估构建状态和资源消耗。

Nebula ​

  • Tag 和 Edge 属性需要显式定义;
  • CREATE ... IF NOT EXISTS 不会自动合并新属性,SDK 的增量应用会检查并执行属性 ADD;
  • Schema 从 MetaD 向数据面传播存在时间窗口;
  • 索引创建后需要重建,属性查询依赖可用索引;
  • Portable Schema 不支持唯一索引;
  • 当前反查不包含完整索引元数据,因此迁移预览必须结合发布记录或原生检查。

回滚策略 ​

DDL 回滚往往不能等同于应用版本回滚。一个可靠方案应分别考虑:

  • 应用回滚:旧版本能否读取扩展后的 Schema;
  • 数据回滚:回填或转换后的数据如何恢复;
  • 索引回滚:删除或重建索引是否影响查询;
  • 结构回滚:后端是否支持安全撤销 DDL;
  • 任务回滚:正在运行的导入任务使用哪个 Schema 版本。

最安全的方式通常是保留兼容旧结构,在确认新版本稳定后再执行收缩。破坏性操作前必须使用数据库原生工具完成备份和恢复演练;Graph SDK 不负责恢复被删除的结构和数据。

常见误区 ​

计划为 ADDITIVE,就一定可以自动上线吗? ​

不一定。新增索引可能消耗大量资源,新增“必填”属性可能需要全量回填,新边类型可能影响抽取和查询。风险等级反映结构差异,不替代容量和业务评估。

Diff 中没有变化,就证明数据库符合业务要求吗? ​

不一定。反查可能不完整,历史数据可能违反业务规则,索引可能尚未就绪,应用和数据库版本也可能不兼容。

可以根据 removals 自动执行删除吗? ​

不可以。反查缺失和真正多余是两回事。删除前必须确认 inspection 完整性、数据量、调用方、备份和审批。

Schema 版本可以直接从数据库反查吗? ​

不应依赖这一点。Graph Schema 的业务版本属于上层发布元数据,当前不会自动编译为后端 DDL。应用应独立持久化版本历史。

生产检查清单 ​

  • 是否明确期望 Schema、实际 Schema 和已发布版本;
  • 是否保存并展示反查完整性、warning 和 unsupported metadata;
  • 是否分别审查 additions、changes 和 removals;
  • 是否检查历史数据能否满足新类型、必填和唯一性要求;
  • 是否为索引构建和数据回填评估容量及时间;
  • 是否使用 Expand and Contract 处理不兼容变化;
  • 是否让新旧应用在发布窗口内保持兼容;
  • 是否将 DDL、数据回填和破坏性清理拆成独立阶段;
  • 是否为 REVIEW_REQUIRED 和 DESTRUCTIVE 设置审批;
  • 是否准备并演练应用、数据和数据库恢复方案;
  • 是否在真实后端运行关键写入、查询和 Explain 回归;
  • 是否记录 Space、Schema 版本、执行人、结果和验证结论。

迁移的第一步是获得可信的数据库当前状态。下一章 Schema 反查 将说明反查结果包含什么、为什么可能不完整,以及如何安全使用它。