第 03 章:架构深度解析
第 03 章:架构深度解析
3.1 总体架构
AgensGraph 在 PostgreSQL 的基础上扩展了图数据库能力,其核心思路是:复用 PostgreSQL 的存储引擎、事务管理和连接管理,在 SQL 层之上增加图查询的解析和执行层。
┌────────────────────────────────────────────────────────────┐
│ 客户端连接层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ psql │ │ agens │ │ JDBC │ │ 驱动程序 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └──────────────┴──────────────┴──────────────┘ │
└────────────────────────┬───────────────────────────────────┘
│
┌────────────────────────▼───────────────────────────────────┐
│ 查询解析层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SQL 解析器 │ │ Cypher 解析器│ │Gremlin 解析器│ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ └──────────────────┴─────────────────┘ │
└────────────────────────┬───────────────────────────────────┘
│
┌────────────────────────▼───────────────────────────────────┐
│ 查询计划层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ SQL 优化器 │ │ 图查询优化器 │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ └──────────┬──────┘ │
│ ┌───────▼───────┐ │
│ │ 统一执行引擎 │ │
│ └───────┬───────┘ │
└────────────────────┼──────────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────────┐
│ 存储引擎层 │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ 堆存储 │ │ B-Tree │ │ 图存储抽象层 │ │
│ │ (Heap) │ │ 索引 │ │ (Graph Storage) │ │
│ └─────────────┘ └─────────────┘ └──────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ WAL 日志 │ │ 缓冲管理 │ │ 事务管理 │ │
│ └─────────────┘ └─────────────┘ └──────────────────┘ │
└───────────────────────────────────────────────────────────┘
3.1.1 组件分层
| 层级 | 组件 | 职责 |
|---|---|---|
| 连接层 | libpq / 驱动 | 客户端连接管理、协议处理 |
| 解析层 | SQL / Cypher / Gremlin 解析器 | 将查询文本转为内部语法树 |
| 重写层 | 查询重写器 | 视图展开、子查询优化、语义检查 |
| 计划层 | 优化器 | 生成最优执行计划(基于代价) |
| 执行层 | 执行器 | 按执行计划访问数据 |
| 存储层 | 存储引擎 | 数据的物理存储和访问 |
| 事务层 | 事务管理器 | ACID 保证、并发控制 |
3.2 存储引擎
3.2.1 顶点和边的物理存储
AgensGraph 将图数据映射到 PostgreSQL 的关系存储结构中:
图的逻辑模型:
Vertex ──[Edge]──▶ Vertex
图的物理存储:
┌─────────────────────────────────────┐
│ ag_vertex 表(系统表) │
│ id (OID) | label | properties │
│ ---------|-------|-----------------│
│ 100001 | Person| {name: "张三"} │
│ 100002 | Person| {name: "李四"} │
└─────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ ag_edge 表(系统表) │
│ id | start_id | end_id | label | properties │
│ ---|----------|--------|-------|------------│
│ 1 | 100001 | 100002 | KNOWS | {since:2020}│
└──────────────────────────────────────────────┘
3.2.2 ID 分配策略
| 元素类型 | ID 格式 | 说明 |
|---|---|---|
| 顶点 | 64-bit 整数 | 由序列(Sequence)生成,全局唯一 |
| 边 | 64-bit 整数 | 由序列(Sequence)生成,全局唯一 |
3.2.3 属性存储
属性采用 JSONB 格式存储在 properties 列中:
-- 内部存储结构(概念性表示)
-- 顶点记录
┌─────────────────────────────────────────────────┐
│ id: 100001 │
│ label_id: 1 (Person) │
│ properties: {"name": "张三", "age": 30} │
└─────────────────────────────────────────────────┘
-- 边记录
┌─────────────────────────────────────────────────┐
│ id: 1 │
│ label_id: 5 (KNOWS) │
│ start_id: 100001 │
│ end_id: 100002 │
│ properties: {"since": 2020} │
└─────────────────────────────────────────────────┘
3.2.4 图遍历的存储层优化
AgensGraph 在存储层为图遍历做了专门优化:
遍历路径: Alice ──KNOWS──▶ Bob ──KNOWS──▶ Carol
传统 RDBMS 方式:
1. 全表扫描找 Alice → O(N)
2. 索引查找 Alice 的边 → O(log N)
3. 通过边找到 Bob → O(1)
4. 索引查找 Bob 的边 → O(log N)
5. 通过边找到 Carol → O(1)
AgensGraph 优化:
1. 索引查找 Alice → O(log N)
2. 直接访问邻接边列表 → O(k) (k = 度数)
3. 沿边直接定位 Bob → O(1)
4. 直接访问 Bob 的邻接边 → O(k)
5. 沿边直接定位 Carol → O(1)
3.3 查询处理流程
3.3.1 Cypher 查询的完整处理流程
一条 Cypher 查询从输入到结果输出,经历以下阶段:
Cypher 查询文本
│
▼
┌───────────────────┐
│ 1. 词法分析 │ 将文本拆分为 Token
│ (Lexer) │ MATCH → Token("MATCH", KEYWORD)
└───────┬───────────┘
▼
┌───────────────────┐
│ 2. 语法分析 │ 构建抽象语法树 (AST)
│ (Parser) │ MATCH (n:Person) → AST 节点
└───────┬───────────┘
▼
┌───────────────────┐
│ 3. 语义分析 │ 验证标签存在、类型检查
│ (Analyzer) │ 解析变量绑定、作用域
└───────┬───────────┘
▼
┌───────────────────┐
│ 4. 逻辑计划 │ 生成逻辑执行计划
│ (Planner) │ 扫描 → 过滤 → 投影
└───────┬───────────┘
▼
┌───────────────────┐
│ 5. 优化 │ 基于代价的优化
│ (Optimizer) │ 选择最优的扫描方式和连接顺序
└───────┬───────────┘
▼
┌───────────────────┐
│ 6. 物理计划 │ 转换为 PostgreSQL 可执行的
│ (Executor) │ 物理操作序列
└───────┬───────────┘
▼
┌───────────────────┐
│ 7. 执行 │ 访问存储引擎、返回结果
│ (Runtime) │
└───────────────────┘
3.3.2 查询计划分析
使用 EXPLAIN 查看查询计划:
-- 设置图路径
SET graph_path = social_network;
-- 查看 Cypher 查询计划
EXPLAIN (VERBOSE, COSTS ON)
MATCH (p:Person)-[:KNOWS]->(friend:Person)
WHERE p.name = '张三'
RETURN friend.name;
典型的执行计划输出:
QUERY PLAN
──────────────────────────────────────────────────────
GraphScan (cost=0.00..25.00 rows=10 width=64)
→ Vertex Scan on Person p (cost=0.00..10.00 rows=1)
→ Index Scan using person_name_idx (cost=0.28..8.29 rows=1)
Filter: (p.name = '张三')
→ Edge Scan on KNOWS (cost=0.00..5.00 rows=5)
→ Vertex Scan on Person friend (cost=0.00..5.00 rows=1)
3.3.3 执行计划中的关键操作
| 操作 | 说明 | 性能特征 |
|---|---|---|
Vertex Scan | 扫描顶点 | 可利用标签索引 |
Edge Scan | 扫描边 | 可利用起始顶点索引 |
GraphScan | 图遍历扫描 | 组合顶点和边扫描 |
Index Scan | 索引扫描 | O(log N) |
Seq Scan | 顺序扫描 | O(N),大数据集时避免 |
Filter | 条件过滤 | 尽早过滤减少数据量 |
Sort | 排序 | 需要额外内存 |
3.4 与 PostgreSQL 的兼容层
3.4.1 兼容性概览
AgensGraph 与 PostgreSQL 的兼容是深度兼容,而非简单替换:
兼容性层次:
┌────────────────────────────────────┐
│ 协议兼容 │ libpq 协议完全兼容 │ ✅ 100%
├───────────────┼────────────────────┤
│ SQL 兼容 │ 标准 SQL + PG 扩展 │ ✅ 99%+
├───────────────┼────────────────────┤
│ 数据类型 │ 所有 PG 数据类型 │ ✅ 100%
├───────────────┼────────────────────┤
│ 索引类型 │ B-Tree / GiST 等 │ ✅ 100%
├───────────────┼────────────────────┤
│ 扩展兼容 │ 部分 PG 扩展可用 │ ⚠️ 需测试
├───────────────┼────────────────────┤
│ 系统目录 │ PG 目录 + 图专用目录 │ ✅ 扩展兼容
└───────────────┴────────────────────┘
3.4.2 系统目录扩展
AgensGraph 在 PG 系统目录之上添加了图专用的系统表:
| 系统表 | 用途 | 示例查询 |
|---|---|---|
ag_graph | 存储所有图的元数据 | SELECT * FROM ag_graph; |
ag_label | 存储标签(顶点/边类型) | SELECT * FROM ag_label; |
ag_vertex | 顶点存储表(每图一张) | 内部使用 |
ag_edge | 边存储表(每图一张) | 内部使用 |
-- 查看所有图
SELECT graphname, graphid FROM ag_graph;
-- 查看某图的所有标签
SELECT labname, labkind
FROM ag_label
WHERE labname NOT LIKE 'ag_%'
ORDER BY labkind;
3.4.3 SQL 与 Cypher 的执行引擎集成
-- SQL 中嵌入 Cypher 查询(概念示意)
-- AgensGraph 提供了将 Cypher 查询包装为 SQL 子查询的能力
-- 方式 1: 使用 Cypher 查询函数
SELECT * FROM cypher('social_network', $$
MATCH (p:Person)-[:KNOWS]->(f:Person)
RETURN p.name AS person, f.name AS friend
$$) AS (person text, friend text);
-- 方式 2: 直接在图路径下使用 Cypher
SET graph_path = social_network;
MATCH (p:Person) RETURN p.name, p.age;
3.4.4 事务共享
图操作和关系操作共享同一个事务上下文:
BEGIN;
-- SQL 操作
INSERT INTO audit_log (action, timestamp)
VALUES ('graph_update', now());
-- Cypher 操作(在同一事务中)
SET graph_path = social_network;
CREATE (:Person {name: '新用户', age: 25});
-- 两者要么同时提交,要么同时回滚
COMMIT;
3.5 连接管理
3.5.1 连接模型
AgensGraph 继承了 PostgreSQL 的进程模型(Process-based Model):
客户端连接模型:
客户端 1 ──┐
客户端 2 ──┼──▶ Postmaster (主进程)
客户端 3 ──┘ │
├── Backend Process 1 (服务客户端 1)
├── Backend Process 2 (服务客户端 2)
└── Backend Process 3 (服务客户端 3)
每个客户端连接分配一个独立的后端进程
每个后端进程约占用 5-10 MB 内存
3.5.2 连接池配置
在生产环境中,建议使用连接池:
# 使用 PgBouncer 作为连接池
# pgbouncer.ini
[databases]
agens = host=127.0.0.1 port=5432 dbname=agens
[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = md5
auth_file = userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 50
3.6 内存架构
AgensGraph 内存布局:
┌─────────────────────────────────────────┐
│ 共享内存 (Shared Memory) │
│ ┌───────────────┐ ┌────────────────┐ │
│ │ Shared Buffer │ │ WAL Buffer │ │
│ │ (数据缓存) │ │ (日志缓存) │ │
│ └───────────────┘ └────────────────┘ │
│ ┌───────────────┐ ┌────────────────┐ │
│ │ Lock Table │ │ CLOG Buffer │ │
│ │ (锁表) │ │ (事务状态) │ │
│ └───────────────┘ └────────────────┘ │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 后端进程私有内存 │
│ ┌───────────────┐ ┌────────────────┐ │
│ │ work_mem │ │ maintenance │ │
│ │ (排序/哈希) │ │ _work_mem │ │
│ └───────────────┘ └────────────────┘ │
│ ┌───────────────┐ │
│ │ 图遍历上下文 │ │
│ │ (遍历状态) │ │
│ └───────────────┘ │
└─────────────────────────────────────────┘
| 内存区域 | 配置参数 | 作用 |
|---|---|---|
| 共享缓冲区 | shared_buffers | 缓存数据页,减少磁盘 I/O |
| WAL 缓冲区 | wal_buffers | WAL 日志写入缓冲 |
| 工作内存 | work_mem | 排序、哈希操作的内存 |
| 维护内存 | maintenance_work_mem | VACUUM、CREATE INDEX 等 |
3.7 本章小结
| 架构要点 | 说明 |
|---|---|
| 存储模型 | 图数据映射为 PG 关系表(ag_vertex / ag_edge) |
| 属性存储 | JSONB 格式 |
| 查询处理 | Cypher → AST → 逻辑计划 → 物理计划 → 执行 |
| 兼容性 | 深度兼容 PostgreSQL(协议/SQL/数据类型/索引) |
| 事务模型 | 图操作与 SQL 操作共享事务 |
| 连接模型 | PG 进程模型,建议使用连接池 |
3.8 练习
- 使用
EXPLAIN分析以下查询的执行计划,并解释各步骤:MATCH (p:Person)-[:KNOWS*1..3]->(fof:Person) WHERE p.name = '张三' RETURN fof.name; - 查询
ag_graph和ag_label系统表,列出所有图和标签。 - 测试在一个事务中混合使用 SQL INSERT 和 Cypher CREATE。