第八章 使用 JSON + SQL 模拟图模型 - 第一节:使用 JSONB 存储节点与边关系
文章目录
第三部分:NoSQL 能力扩展实战
第八章 使用 JSON + SQL 模拟图模型
第一节 使用 JSONB 存储节点与边关系
目标:学习一种不依赖
ltree或 Apache AGE 等专门扩展,仅使用 PostgreSQL 内核功能(特别是JSONB和递归查询)来模拟图数据模型的方法。
虽然 Apache AGE 提供了完整的图数据库体验,但在某些场景下,我们可能因为以下原因无法使用它:
- 环境限制:无法在生产环境中安装第三方扩展。
- 轻量级需求:图模型相对简单,引入一个完整的图数据库扩展显得“杀鸡用牛刀”。
- 数据一致性:希望将图的边信息与节点自身的数据更紧密地耦合在一起。
在这种情况下,我们可以利用 PostgreSQL 强大的 JSONB 数据类型和 SQL 功能,来巧妙地模拟一个图数据库。这种方法的核心思想是:用表来存节点,用 JSONB 字段来存边。
数据建模思路
我们将创建一个 nodes 表来存储所有的图节点(人、公司等)。表中的每一行代表一个节点。
- 节点属性:使用
JSONB类型的properties字段来存储节点的各种属性(如姓名、年龄),这提供了极大的灵活性。 - 边关系:同样使用
JSONB类型的edges字段来存储该节点出发的所有出向边(Outgoing Edges)。
🏛️ 第一步:设计数据表
| |
edges 字段的 JSONB 结构设计:
我们把 edges 设计成一个 JSON 数组,数组中的每个对象代表一条边。
| |
type: 边的类型。target_id: 边指向的目标节点的id。properties: 边自身的属性。
✍️ 第二步:插入图数据
让我们用这个模型来构建之前章节中使用的社交网络。
| |
优缺点分析
优点:
- 无外部依赖:只使用 PostgreSQL 核心功能,部署和维护简单。
- 数据聚合:一个节点及其所有出向边的信息都存储在同一行中,对于某些“以节点为中心”的查询非常方便。
- 高度灵活:
JSONB的无模式特性使得添加新的节点和边属性变得异常简单。
缺点:
- 查询复杂:图遍历需要编写复杂的递归 SQL 查询,远不如 Cypher 直观。
- 反向边查询低效:要查找指向某个节点的所有入向边(Incoming Edges),需要扫描整个表的
edges字段,这是一个昂贵的操作。 - 数据冗余与一致性:边信息存储在源节点中,如果目标节点被删除,需要应用层逻辑来清理悬挂的边,以保证数据一致性。
📌 小结
使用 JSONB 和一张表来模拟图,是一种在特定约束条件下的“权变之策”。它将图的结构信息“编码”到关系型表的字段中,通过 JSONB 的灵活性弥补了关系模型的不足。
这种方法最适合以下场景:
- 图的规模不大。
- 查询模式主要是从已知节点出发进行遍历。
- 对入向边的查询需求较少。
- 无法或不愿引入专门的图数据库扩展。
在下一节,我们将直面这种模型的最大挑战:如何使用递归 SQL 来实现图的遍历和查询。
