AgensGraph 完全指南 / 第 01 章:AgensGraph 与图数据库基础
第 01 章:AgensGraph 与图数据库基础
1.1 为什么需要图数据库?
1.1.1 关系型数据库的局限
传统关系型数据库(RDBMS)在处理高度关联数据时面临天然的瓶颈。考虑一个典型场景:在社交网络中查找"朋友的朋友"。
-- 关系型方案:多层 JOIN
SELECT DISTINCT u3.name
FROM users u1
JOIN friendships f1 ON u1.id = f1.user_id
JOIN users u2 ON f1.friend_id = u2.id
JOIN friendships f2 ON u2.id = f2.user_id
JOIN users u3 ON f2.friend_id = u3.id
WHERE u1.name = '张三'
AND u3.id != u1.id;
当查询深度增加到 3 层、5 层甚至更多时,JOIN 的数量呈指数级增长,查询性能急剧下降。这正是关系型模型的"关联诅咒"(Join Curse)。
| 查询深度 | 关系型 JOIN 数 | 图数据库遍历复杂度 |
|---|---|---|
| 2 层朋友 | 4 个 JOIN | O(k²) |
| 3 层朋友 | 6 个 JOIN | O(k³) |
| 5 层朋友 | 10 个 JOIN | O(k⁵) |
| N 层关系 | 2N 个 JOIN | O(kᴺ) |
注:k 为平均邻居数(average degree),图数据库的复杂度虽然也是指数级,但常数因子远小于 JOIN 操作。
1.1.2 图数据库的核心思想
图数据库将数据之间的关系视为"一等公民"(First-Class Citizen),与数据本身同等重要。其核心理念:
- 关系是预物化的:不需要运行时通过 JOIN 计算,关系在写入时已经存储
- 遍历是 O(1) 操作:沿关系导航的代价与数据总量无关,只与遍历深度有关
- Schema 灵活:图结构天然适合渐进式演化
1.1.3 图数据库 vs 关系型数据库
| 维度 | 关系型数据库 | 图数据库 |
|---|---|---|
| 数据模型 | 表格(行/列) | 图(顶点/边) |
| 关系表示 | 外键 + JOIN | 原生边(直接指针) |
| 查询语言 | SQL | Cypher / Gremlin / GQL |
| Schema | 严格 Schema | 灵活 / Schema-free |
| 擅长场景 | 结构化事务、报表 | 关系分析、路径查找 |
| 扩展方式 | 垂直扩展为主 | 水平扩展(部分产品) |
| 数据局部性 | 行存储 | 图遍历局部性 |
1.2 图数据库核心概念
1.2.1 Property Graph 模型
AgensGraph 采用 属性图模型(Property Graph Model),这是目前最主流的图数据模型。一个属性图由以下元素组成:
┌──────────────┐ knows_since: 2020 ┌──────────────┐
│ Vertex │ ─────────────────────▶ │ Vertex │
│ label: Person │ label: Person
│ { │ {
│ name: "Alice", │ name: "Bob",
│ age: 30 │ age: 28
│ } │ }
└──────────────┘ └──────────────┘
核心元素:
| 元素 | 英文 | 说明 |
|---|---|---|
| 顶点 | Vertex / Node | 图中的实体,用圆形表示 |
| 边 | Edge / Relationship | 顶点之间的关系,用箭头表示 |
| 标签 | Label | 顶点或边的类型/分类标记 |
| 属性 | Property | 顶点或边上携带的键值对数据 |
形式化定义:
属性图 G = (V, E, λ, σ)
V: 顶点集合
E: 边集合 (E ⊆ V × V)
λ: 标签函数 — 为顶点和边分配标签
σ: 属性函数 — 为顶点和边分配键值属性
1.2.2 顶点(Vertex)
顶点是图中的基本实体单元,具有以下特征:
- 唯一标识:每个顶点在图中有唯一 ID
- 标签(Label):一个或多个类型标记,如
Person、Employee - 属性(Properties):任意数量的键值对
-- 创建一个带标签和属性的顶点
CREATE (a:Person:Employee {
name: '张三',
age: 30,
department: '技术部',
join_date: date('2022-03-15')
})
RETURN a;
1.2.3 边(Edge)
边表示顶点之间的关系,具有以下特征:
- 有方向:每条边都有起始顶点和终止顶点
- 唯一标识:每条边有唯一 ID
- 单一类型:每条边有一个标签(关系类型)
- 可带属性:与顶点一样可以携带属性
-- 创建带属性的边
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})
CREATE (a)-[r:KNOWS {since: 2020, context: '大学同学'}]->(b)
RETURN r;
1.2.4 标签(Label)
标签是对顶点或边进行分类的机制,类似于面向对象编程中的"类"(Class)。
标签特性:
├─ 一个顶点可以有多个标签 → (:Person:Employee:Manager)
├─ 标签是可选的 → 可以有无标签的顶点
├─ 标签不等于类型 → 同一标签的顶点可以有不同的属性
└─ 标签用于索引和查询优化 → 可在标签上创建索引
1.2.5 属性(Property)
属性是以键值对形式存储的数据,其中:
- 键(Key):字符串类型
- 值(Value):支持多种数据类型
| 数据类型 | 示例 | 说明 |
|---|---|---|
| Integer | 42 | 整数 |
| Float | 3.14 | 浮点数 |
| String | 'hello' | 字符串 |
| Boolean | true | 布尔值 |
| Array | [1, 2, 3] | 数组 |
| Map | {k: 'v'} | 嵌套映射 |
| Date | date('2024-01-01') | 日期 |
| Timestamp | datetime() | 时间戳 |
1.3 图数据库全景
1.3.1 主流图数据库对比
| 数据库 | 类型 | 查询语言 | 许可 | 存储模型 | 适合场景 |
|---|---|---|---|---|---|
| AgensGraph | 多模型 | Cypher + Gremlin + SQL | Apache 2.0 | PG 存储引擎 | 企业级混合负载 |
| Neo4j | 原生图 | Cypher | GPL/商业 | 原生图存储 | 中小规模图应用 |
| JanusGraph | 分布式图 | Gremlin | Apache 2.0 | 可插拔后端 | 大规模分布式图 |
| Amazon Neptune | 托管服务 | SPARQL + Gremlin | 商业 | 云托管 | AWS 生态 |
| ArangoDB | 多模型 | AQL | Apache 2.0 | 多模型引擎 | 多模型混合 |
| TigerGraph | 分布式图 | GSQL | 商业 | 原生并行图 | 超大规模分析 |
1.3.2 AgensGraph 的独特定位
AgensGraph 的核心差异化在于PostgreSQL 深度融合:
传统方案(双数据库):
┌─────────────┐ API ┌─────────────┐
│ PostgreSQL │ ◀─────────▶ │ Neo4j │
│ (关系数据) │ │ (图数据) │
└─────────────┘ └─────────────┘
→ 数据同步复杂、一致性难保证、运维成本高
AgensGraph 方案(单数据库):
┌─────────────────────────────────────┐
│ AgensGraph │
│ ┌───────────┐ ┌───────────────┐ │
│ │ SQL 引擎 │ │ 图查询引擎 │ │
│ └─────┬─────┘ └──────┬────────┘ │
│ └──────┬────────┘ │
│ 共享存储引擎 │
│ 共享事务管理 │
└─────────────────────────────────────┘
→ 统一事务、零数据同步、单一运维入口
1.4 AgensGraph 发展历程
| 时间 | 里程碑 |
|---|---|
| 2016 | Bitnine 发布 AgensGraph 1.0,基于 PostgreSQL 9.5 |
| 2017 | 引入 Cypher 查询语言支持 |
| 2018 | 升级到 PostgreSQL 10,支持声明式分区 |
| 2019 | 引入 Apache TinkerPop / Gremlin 支持 |
| 2020 | AgensGraph 2.x 发布,基于 PostgreSQL 12 |
| 2022 | 增强 SQL-Cypher 混合查询能力 |
| 2023 | AgensGraph 2.13 发布,性能大幅优化 |
1.5 第一个图:Hello Graph
在深入学习之前,先通过一个简单例子感受图查询的魅力。
场景:小型社交网络
-- 创建人物节点
CREATE (alice:Person {name: 'Alice', age: 30});
CREATE (bob:Person {name: 'Bob', age: 28});
CREATE (carol:Person {name: 'Carol', age: 32});
CREATE (dave:Person {name: 'Dave', age: 25});
-- 创建关系
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020}]->(b);
MATCH (a:Person {name: 'Alice'}), (c:Person {name: 'Carol'})
CREATE (a)-[:KNOWS {since: 2019}]->(c);
MATCH (b:Person {name: 'Bob'}), (c:Person {name: 'Carol'})
CREATE (b)-[:KNOWS {since: 2021}]->(c);
MATCH (c:Person {name: 'Carol'}), (d:Person {name: 'Dave'})
CREATE (c)-[:KNOWS {since: 2022}]->(d);
查询:找到 Alice 的朋友的朋友
MATCH (alice:Person {name: 'Alice'})-[:KNOWS*2]->(fof:Person)
WHERE fof.name <> 'Alice'
RETURN DISTINCT fof.name AS friend_of_friend;
查询结果
| friend_of_friend |
|---|
| Carol |
| Dave |
注意:Bob 是 Alice 的直接朋友,不会出现在"朋友的朋友"结果中(除非通过其他路径也是 2 跳可达)。
对比 SQL 实现
-- 等价 SQL 查询(假设关系存储在 friendships 表中)
SELECT DISTINCT p3.name AS friend_of_friend
FROM persons p1
JOIN friendships f1 ON p1.id = f1.person_id
JOIN persons p2 ON f1.friend_id = p2.id
JOIN friendships f2 ON p2.id = f2.person_id
JOIN persons p3 ON f2.friend_id = p3.id
WHERE p1.name = 'Alice'
AND p3.name != 'Alice'
AND p3.id NOT IN (
SELECT f.friend_id FROM friendships f
JOIN persons p ON f.person_id = p.id
WHERE p.name = 'Alice'
);
差距显而易见——Cypher 更直观、更简洁、更接近人类对"关系"的思维方式。
1.6 本章小结
| 要点 | 说明 |
|---|---|
| 图数据库核心优势 | 关系预物化,遍历高效 |
| Property Graph 模型 | 顶点 + 边 + 标签 + 属性 |
| AgensGraph 定位 | 基于 PostgreSQL 的多模型图数据库 |
| 核心查询语言 | Cypher(声明式)+ Gremlin(命令式)+ SQL |
| 最大优势 | 图与关系的原生融合,单一数据库承载混合负载 |
1.7 练习
- 概念辨析:解释 Vertex、Edge、Label、Property 之间的关系。
- 场景分析:以下场景哪些适合用图数据库?说明理由。
- 电商订单管理
- 社交网络推荐
- 库存管理
- 知识图谱问答
- 动手实践:使用
CREATE语句创建一个包含 5 个顶点和 6 条边的小型图。