强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

AgensGraph 完全指南 / 第 15 章:最佳实践与规范

第 15 章:最佳实践与规范

15.1 图建模规范

15.1.1 命名规范

元素 规范 正确示例 错误示例
顶点标签 PascalCase,单数 Person, OrderItem persons, order_items
边标签 UPPER_SNAKE_CASE KNOWS, WORKS_AT knows, worksAt
属性名 snake_case first_name, created_at firstName, createdAt
图名 snake_case social_network SocialNetwork
约束名 {table}_{col}_{type} person_email_unique constraint1
索引名 {table}_{col}_idx person_name_idx idx1

15.1.2 标签设计原则

标签设计四原则:

  1. 业务语义: 标签应反映业务实体
     ✅ :Customer, :Product, :Order
     ❌ :Table1, :Data, :Record

  2. 适度粒度: 2-3 个标签为宜
     ✅ :Person:Employee
     ❌ :Person:Employee:Manager:FullTime:BeijingOffice:TechDept

  3. 不可变性: 标签不应频繁变化
     ✅ :Person (status 属性区分 active/inactive)
     ❌ :ActivePerson, :InactivePerson

  4. 查询驱动: 标签服务于高频查询
     ✅ :VIP_Customer (VIP 客户查询频繁)
     ❌ 将所有分类信息都做成标签

15.1.3 边设计原则

边设计五原则:

  1. 有向性: 边必须有方向
     (User)-[:PLACED]->(Order)
     (Order)-[:CONTAINS]->(Product)

  2. 语义化: 边标签应表达关系含义
     ✅ :REPORTS_TO, :PURCHASED, :REVIEWED
     ❌ :HAS, :RELATES_TO, :LINKED

  3. 适度属性: 边上存储关系属性
     (User)-[:PURCHASED {date, quantity, price}]->(Product)

  4. 避免超级节点: 大量边的节点需特殊处理
     ❌ (PopularProduct)<-[:VIEWED]-(百万用户)
     ✅ 按时间分区或使用聚合

  5. Reification: 复杂关系提升为顶点
     ❌ (Buyer)-[:BOUGHT {items, total, address}]->(Seller)
     ✅ (Buyer)-[:PLACED]->(Order)-[:CONTAINS]->(Product)

15.1.4 属性设计原则

-- ✅ 良好的属性设计
CREATE (:Person {
  id: 'P001',                      -- 业务主键
  name: '张三',                     -- 必填属性
  email: '[email protected]',   -- 唯一标识
  age: 30,                         -- 基本属性
  skills: ['Java', 'Python'],      -- 数组属性
  address: {                       -- 嵌套结构
    city: '北京',
    district: '海淀区',
    street: '中关村大街1号'
  },
  created_at: datetime(),          -- 时间戳
  updated_at: datetime(),          -- 更新时间
  version: 1                       -- 乐观锁版本号
});
原则 说明 示例
原子性 属性值不可再分 first_name + last_name 而非 full_name
类型一致 同名属性保持类型统一 age 始终为 Integer
避免大对象 不存储二进制/长文本 使用 URL 引用外部存储
索引友好 高频查询字段作为属性 将 status 而非 备注 做索引
时间标记 记录创建和修改时间 created_at, updated_at

15.2 查询编写规范

15.2.1 Cypher 编码规范

-- ✅ 推荐: 格式化良好的查询
MATCH (u:User {status: 'active'})-[:PLACED]->(o:Order)-[:CONTAINS]->(p:Product)
WHERE o.created_at > datetime('2024-01-01')
  AND p.category = 'electronics'
WITH u, count(o) AS order_count, sum(o.total) AS total_spent
WHERE order_count > 5
RETURN
  u.name AS customer,
  order_count,
  total_spent
ORDER BY total_spent DESC
LIMIT 20;

-- ❌ 不推荐: 所有内容挤在一行
-- MATCH (u:User{status:'active'})-[:PLACED]->(o:Order)-[:CONTAINS]->(p:Product) WHERE o.created_at>datetime('2024-01-01') AND p.category='electronics' WITH u,count(o) AS order_count,sum(o.total) AS total_spent WHERE order_count>5 RETURN u.name AS customer,order_count,total_spent ORDER BY total_spent DESC LIMIT 20;

15.2.2 查询编写 Checklist

检查项 说明 严重程度
✅ 设置 graph_path 执行前确保图路径已设置 🔴 必须
✅ 使用索引属性定位 MATCH 中使用已索引属性 🟠 重要
✅ 限制遍历深度 变长路径设置合理的 *N..M 🟠 重要
✅ 使用 LIMIT 防止返回过多结果 🟡 建议
✅ 参数化查询 防止 Cypher 注入 🔴 必须
✅ 只返回必要字段 避免 RETURN * 🟡 建议
✅ 使用 MERGE 去重 防止重复数据 🟠 重要
✅ 事务粒度适当 避免长事务 🟠 重要

15.2.3 参数化查询

# ✅ 正确: 参数化查询
query = """
    MATCH (p:Person {name: $name})
    RETURN p.age, p.city
"""
params = {'name': 'Alice'}
result = execute_cypher(query, params)

# ❌ 错误: 字符串拼接(有注入风险)
query = f"""
    MATCH (p:Person {{name: '{user_input}'}})
    RETURN p.age, p.city
"""

15.3 索引管理规范

15.3.1 索引创建策略

-- 必须创建索引的场景:
-- 1. 主键/唯一标识
CREATE CONSTRAINT person_id_unique
  ON (p:Person) ASSERT p.id IS UNIQUE;

-- 2. 高频查询属性
CREATE INDEX ON :Person(name);
CREATE INDEX ON :Person(email);

-- 3. 外键/关联属性
CREATE INDEX ON :Order(user_id);
CREATE INDEX ON :KNOWS(since);

-- 4. 复合查询
CREATE INDEX ON :Person(city, age);

15.3.2 索引维护计划

频率 操作 说明
每日 检查索引使用率 删除未使用索引
每周 ANALYZE 更新统计 保持优化器准确
每月 REINDEX 重建索引 消除索引碎片
每季 索引策略评审 根据查询变化调整
-- 查看索引使用情况
SELECT
    indexrelname AS index_name,
    idx_scan AS scans,
    idx_tup_read AS tuples_read,
    pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;

-- 查找未使用的索引(可能需要删除)
SELECT
    indexrelname,
    idx_scan,
    pg_size_pretty(pg_relation_size(indexrelid)) AS wasted_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
  AND indexrelname NOT LIKE '%pkey%'
ORDER BY pg_relation_size(indexrelid) DESC;

15.4 安全规范

15.4.1 访问控制

-- 创建只读用户
CREATE USER readonly_user WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE agens TO readonly_user;
GRANT USAGE ON SCHEMA social_network TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA social_network TO readonly_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA social_network
  GRANT SELECT ON TABLES TO readonly_user;

-- 创建应用用户(最小权限原则)
CREATE USER app_user WITH PASSWORD 'app_secure_password';
GRANT CONNECT ON DATABASE agens TO app_user;
GRANT USAGE ON SCHEMA social_network TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA social_network TO app_user;

-- 创建管理员用户
CREATE USER admin_user WITH PASSWORD 'admin_secure_password' SUPERUSER;

15.4.2 安全配置清单

配置项 推荐值 说明
password_encryption scram-sha-256 使用强密码加密
ssl on 启用 SSL 连接
ssl_min_protocol_version TLSv1.2 最低 TLS 版本
pg_hba.conf 限制 IP 范围 不使用 0.0.0.0/0
log_connections on 记录连接事件
log_disconnections on 记录断开事件
log_statement ddl 记录 DDL 语句

15.4.3 SSL 配置

# agens.conf
ssl = on
ssl_cert_file = '/etc/ssl/server.crt'
ssl_key_file = '/etc/ssl/server.key'
ssl_ca_file = '/etc/ssl/ca.crt'
ssl_crl_file = '/etc/ssl/root.crl'
ssl_min_protocol_version = 'TLSv1.2'
ssl_ciphers = 'HIGH:!aNULL:!MD5'
# 生成自签名证书(测试环境)
openssl req -new -x509 -days 365 -nodes \
  -text -out server.crt \
  -keyout server.key \
  -subj "/CN=agensgraph.local"

# 设置权限
chmod 600 server.key
chown agens:agens server.*

15.5 备份与恢复规范

15.5.1 备份策略矩阵

备份类型 频率 保留周期 存储位置 适用场景
完整备份 每周日 4 周 异地存储 全量恢复
增量备份 每天 7 天 本地+异地 快速恢复
WAL 归档 持续 7 天 异地存储 PITR 恢复
逻辑备份 每天 30 天 对象存储 跨版本迁移

15.5.2 备份验证

#!/bin/bash
# verify_backup.sh - 备份验证脚本

BACKUP_FILE=$1
VERIFY_PORT=5433

echo "=== Backup Verification ==="

# 1. 恢复到临时实例
docker run -d --name ag_verify \
  -p ${VERIFY_PORT}:5432 \
  -e AG_PASSWORD=verify123 \
  bitnine/agensgraph:v2.13

sleep 10

# 2. 恢复备份
pg_restore -h localhost -p ${VERIFY_PORT} -U agens -d agens ${BACKUP_FILE}

# 3. 数据完整性检查
TABLE_COUNT=$(psql -h localhost -p ${VERIFY_PORT} -U agens -t -c "
  SELECT count(*) FROM information_schema.tables
  WHERE table_schema = 'public';
")

TOTAL_ROWS=$(psql -h localhost -p ${VERIFY_PORT} -U agens -t -c "
  SELECT sum(n_live_tup) FROM pg_stat_user_tables;
")

echo "Tables: ${TABLE_COUNT}"
echo "Total rows: ${TOTAL_ROWS}"

# 4. 基本查询测试
psql -h localhost -p ${VERIFY_PORT} -U agens -c "
  SET graph_path = social_network;
  MATCH (p:Person) RETURN count(p);
"

# 5. 清理
docker stop ag_verify && docker rm ag_verify

echo "=== Verification Complete ==="

15.5.3 恢复演练

演练项目 频率 验证内容
完整恢复 每季 全量备份恢复到新环境
PITR 恢复 每季 恢复到指定时间点
主从切换 每季 主库故障,备库提升
跨区域恢复 每半年 异地备份恢复测试

15.6 监控规范

15.6.1 监控指标分级

级别 指标 阈值 告警方式
P0 (紧急) 服务不可用 无法连接 短信 + 电话
P0 (紧急) 磁盘使用率 > 90% 短信 + 电话
P1 (严重) 复制延迟 > 30s 短信
P1 (严重) 缓冲区命中率 < 95% 短信
P2 (警告) 连接数 > 80% max 邮件
P2 (警告) 慢查询数量 > 10/min 邮件
P3 (信息) 表膨胀率 > 30% 工单

15.6.2 Prometheus 告警规则

# prometheus/alert_rules.yml
groups:
  - name: agensgraph_alerts
    rules:
      - alert: AgensGraphDown
        expr: pg_up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "AgensGraph instance is down"

      - alert: HighDiskUsage
        expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Disk usage above 90%"

      - alert: LowBufferHitRatio
        expr: pg_stat_database_blks_hit_ratio < 0.95
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Buffer hit ratio below 95%"

      - alert: HighReplicationLag
        expr: pg_stat_replication_lag > 30
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Replication lag above 30 seconds"

      - alert: TooManyConnections
        expr: pg_stat_activity_count > (pg_settings_max_connections * 0.8)
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Connection count above 80% of max"

15.7 团队协作规范

15.7.1 版本控制

图 Schema 变更流程:

  1. 开发环境测试变更
      │
  2. 编写迁移脚本 (migration script)
      │
  3. Code Review
      │
  4. 测试环境验证
      │
  5. 生产环境执行
      │
  6. 验证结果

15.7.2 迁移脚本管理

migrations/
├── V001__create_graph_social_network.cypher
├── V002__add_person_constraints.cypher
├── V003__create_indexes.cypher
├── V004__add_company_label.cypher
└── V005__migrate_person_data.cypher
-- V001__create_graph_social_network.cypher
-- 创建图数据库
CREATE GRAPH IF NOT EXISTS social_network;
SET graph_path = social_network;

-- 创建标签约束
CREATE CONSTRAINT person_id_unique
  ON (p:Person) ASSERT p.id IS UNIQUE;

CREATE CONSTRAINT company_id_unique
  ON (c:Company) ASSERT c.id IS UNIQUE;

15.7.3 开发环境管理

# docker-compose.dev.yml
version: '3.8'

services:
  agensgraph:
    image: bitnine/agensgraph:v2.13
    ports:
      - "5432:5432"
    environment:
      AG_PASSWORD: dev123
    volumes:
      - ./data:/home/agens/AgensGraph/data
      - ./migrations:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U agens"]
      interval: 5s
      timeout: 3s
      retries: 5

  # 每个开发者使用独立端口
  # developer1: 5432
  # developer2: 5433
  # developer3: 5434

15.7.4 文档规范

# 图模型文档模板

## 1. 概述
- 图名称: social_network
- 用途: 社交网络关系管理
- 负责人: 张三
- 创建时间: 2024-01-01

## 2. 顶点定义
| 标签 | 属性 | 约束 | 说明 |
|------|------|------|------|
| Person | id, name, email, age | id UNIQUE | 用户 |
| Company | id, name, industry | id UNIQUE | 公司 |

## 3. 边定义
| 类型 | 起点 | 终点 | 属性 | 说明 |
|------|------|------|------|------|
| KNOWS | Person | Person | since, context | 好友关系 |
| WORKS_AT | Person | Company | position, since | 工作关系 |

## 4. 索引
| 名称 | 类型 | 说明 |
|------|------|------|
| person_name_idx | B-Tree | Person.name 查询 |
| person_email_idx | B-Tree | Person.email 查询 |

## 5. 变更记录
| 日期 | 版本 | 变更内容 | 操作人 |
|------|------|---------|--------|
| 2024-01-01 | v1.0 | 初始版本 | 张三 |
| 2024-02-15 | v1.1 | 添加 Company 标签 | 李四 |

15.8 生产部署清单

15.8.1 上线前检查清单

分类 检查项 状态 备注
基础配置
PostgreSQL 版本确认 ☐
内存参数调优 ☐
连接池配置 ☐
SSL 启用 ☐
日志配置 ☐
数据模型
图 Schema 设计评审 ☐
约束创建(唯一性/存在性) ☐
索引创建 ☐
初始数据导入验证 ☐
安全
用户权限最小化 ☐
密码策略合规 ☐
网络访问限制 ☐
审计日志开启 ☐
备份恢复
完整备份策略配置 ☐
WAL 归档配置 ☐
备份恢复测试 ☐
PITR 恢复测试 ☐
监控告警
监控指标采集 ☐
告警规则配置 ☐
告警渠道测试 ☐
仪表板创建 ☐
高可用
主从复制配置 ☐
复制延迟监控 ☐
故障切换演练 ☐
自动故障转移 ☐
性能
基准测试完成 ☐
慢查询阈值设定 ☐
查询优化评审 ☐
容量规划 ☐

15.8.2 灾难恢复预案

场景 RTO RPO 恢复步骤
主库硬件故障 30min 0 备库提升为主库
数据损坏 2h < 5min PITR 恢复
机房级故障 4h < 1h 异地恢复
误删数据 30min 0 闪回查询/PITR

15.8.3 运维值班手册

# 故障处理流程

## P0 故障(服务不可用)
1. 确认故障范围和影响
2. 启动应急预案
3. 通知相关方
4. 执行故障恢复
5. 验证服务恢复
6. 故障复盘

## P1 故障(性能下降)
1. 检查监控指标
2. 定位瓶颈(CPU/内存/IO/锁)
3. 应急优化(杀慢查询/扩连接)
4. 根因分析
5. 制定长期优化方案

## P2 故障(功能异常)
1. 收集错误信息
2. 定位问题代码/配置
3. 修复并测试
4. 部署上线
5. 验证恢复

15.9 性能优化速查卡

┌─────────────────────────────────────────────────────┐
│           AgensGraph 性能优化速查卡                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  [查询层]                                            │
│  ✅ 使用索引属性定位起始节点                            │
│  ✅ 限制变长路径深度 *1..N                             │
│  ✅ 尽早过滤 (WHERE 在 MATCH 中)                      │
│  ✅ 返回必要字段,使用 LIMIT                          │
│  ✅ 避免笛卡尔积(多个独立 MATCH)                     │
│                                                     │
│  [索引层]                                            │
│  ✅ 高频查询属性创建索引                               │
│  ✅ 复合索引:等值列在前,范围列在后                    │
│  ✅ 定期 ANALYZE 更新统计                             │
│  ✅ 清理未使用的索引                                  │
│                                                     │
│  [配置层]                                            │
│  ✅ shared_buffers = RAM × 25%                       │
│  ✅ work_mem = 16-64MB (按需调整)                     │
│  ✅ effective_cache_size = RAM × 75%                 │
│  ✅ random_page_cost = 1.1 (SSD)                     │
│                                                     │
│  [运维层]                                            │
│  ✅ 使用连接池(PgBouncer)                           │
│  ✅ 监控慢查询和锁等待                                │
│  ✅ 定期 VACUUM 和 REINDEX                           │
│  ✅ 容量规划和趋势分析                                │
│                                                     │
└─────────────────────────────────────────────────────┘

15.10 本章小结

规范领域 核心要点
图建模 命名规范、标签适度、属性原子、避免超级节点
查询编写 参数化、索引利用、深度限制、返回精简
索引管理 策略性创建、定期维护、清理未使用索引
安全 最小权限、SSL 加密、审计日志
备份 多级策略、定期演练、异地存储
监控 分级告警、关键指标、自动化工单
团队协作 版本控制、迁移脚本、文档模板
生产部署 检查清单、灾备预案、值班手册

15.11 扩展阅读