图机器学习(21)——构建可扩展图计算应用

发布于:2025-07-27 ⋅ 阅读:(17) ⋅ 点赞:(0)

0. 前言

我们已经学习了如何设计并实现基于图结构的机器学习模型。然而除了算法设计之外,将建模/分析流程嵌入健壮可靠的端到端应用同样至关重要——这在以构建生产级系统为目标、需要支撑数据驱动决策或实时信息服务的工业场景中尤为关键。本节将系统阐述构建可扩展图计算应用的核心概念与框架体系。
我们首先介绍 Lambda 架构的总体框架,该架构为需要大规模批处理与实时更新的可扩展应用提供了设计范式。随后将该框架应用于图计算领域,重点解析其两大分析组件:图处理引擎与图查询引擎。通过对比共享内存与分布式内存环境下的技术实现,揭示各类技术的共性与特性。

1. Lambda 架构概述

近年来,构建可扩展架构成为重点研究方向,这类架构需要同时满足两大核心需求:一方面能处理海量数据,另一方面能基于最新可用信息实时生成响应。
此外,这些系统还需具备无缝扩展能力——无论是通过水平扩展(增加服务器数量)还是垂直扩展(提升服务器性能)来应对用户规模或数据量的增长。Lambda 架构正是一种专为海量数据处理设计的数据处理架构,它能以极高效率保障系统吞吐量,同时保持低延迟、实现容错能力,并将误差控制在可忽略范围内。
Lambda 架构由三层构成:

  • 批处理层 (Batch Layer):该层构建于(可分布式扩展的)存储系统之上,负责存储全量历史数据,并对完整数据集执行在线分析处理 (Online Analytical Processing, OLAP)。新数据持续摄入存储,其工作模式与传统数据仓库系统类似。该层通过大规模并行作业实现海量数据处理,完成数据聚合、结构化及关键信息计算。在机器学习场景中,依赖历史数据的模型训练通常在此层完成,生成的训练模型可用于批量预测任务或实时执行。
  • 加速层 (Speed Layer):作为低延迟处理层,该层通过流式处理实现信息的实时计算,确保及时更新与响应。其计算过程通常轻量快速,无需长时间运算或高负载。该层输出会与批处理层生成的数据进行(近)实时整合,为在线事务处理 (Online Transaction Processing, OLTP) 提供支持。加速层往往复用 OLAP 的计算结果(如训练好的模型)——例如信用卡交易中的实时欺诈检测引擎,就在加速层部署训练模型以实现即时预测并触发欺诈警报。该层技术可基于事件驱动(如 Apache Storm )或微批处理(如 Spark Streaming),根据应用场景在延迟、容错和计算速度方面呈现差异化特性。
  • 服务层 (Serving Layer):该层负责对批处理层和加速层的数据进行组织、结构化与索引,实现快速检索。其核心使命是将批处理层的稳态输出与加速层的最新实时信息融合,为用户提供统一连贯的数据视图。服务层通常由持久化存储构成,整合历史聚合数据与实时更新,可采用关系型或非关系型数据库,并通过优化索引降低延迟、提升查询效率。数据可通过两种方式对外暴露:一是直接连接数据库并使用特定领域查询语言(如 SQL);二是通过 RESTful API 等专用服务( Python 中可用 FlaskFastAPITurboGear 等框架快速实现),经由定制化接口提供数据服务。

Lambda 架构

Lambda 架构具有诸多优势,这些优势推动并促进了其在大数据应用中的广泛采用:

  • 无服务器管理:Lambda 架构设计模式通常将功能层进行抽象,无需安装、维护或管理任何软件/基础设施
  • 弹性扩展:可通过独立控制批处理层(如计算节点)和加速层(如 Kafka 代理)的处理单元数量实现自动或手动扩展
  • 内置高可用性:其无服务器设计特性天然具备可用性和容错保障
  • 业务敏捷性:能实时响应业务/市场场景变化

尽管功能强大且灵活,Lambda 架构仍存在一些局限性,主要源于批处理层与加速层这两个相互关联的处理流程。这可能导致开发者需要为批处理和流式流程分别构建和维护代码库,从而增加系统复杂度与代码冗余,进而引发调试困难、逻辑不一致和缺陷传导等问题。

本节中,我们简要概述了 Lambda 架构及其基本构建块。在下一小节中,我们将探讨如何为图计算应用实现 Lambda 架构,重点解析核心组件并介绍主流技术方案。

2. Lambda 架构设计

在构建可扩展的、基于图结构的数据驱动应用时,Lambda 架构的功能划分同样体现在分析流程的两个核心组件上,如下图所示:

Lambda架构

  • 图处理引擎:负责执行图结构计算,包括提取特征(如嵌入),计算统计信息(如度分布、边数和团)、计算指标和关键绩效指标 (KPI) (如中心性度量和聚类系数),识别相关的子图(例如,社区),这些操作通常需要 OLAP (联机分析处理)
  • 图查询引擎:允许我们持久化网络数据(通常通过图数据库实现),提供快速信息检索、高效查询及图遍历能力(通常借助图查询语言)。所有数据均已持久化存储在某种数据存储系统(可能基于内存或非内存架构)中,除可能的最终聚合运算外无需额外计算。在此场景下,索引技术对实现高性能与低延迟查询至关重要

图处理引擎部署于批处理层之上,其产出结果可存储并索引至适用的图数据库中。这些数据库作为图查询引擎的后端支撑,能快速高效地提取相关信息,为服务层提供实时可操作视图。根据具体应用场景及图数据规模,通常将图处理引擎和图查询引擎部署在同一基础设施上。
相较于将图数据存储在底层存储系统(如文件系统、HDFSS3),现有图数据库可同时支持联机分析处理 (OLAP) 与联机事务处理 (OLTP)。这类数据库既能作为后端持久化层,存储批处理层加工的历史数据与速度层的实时更新,又能为服务层提供高效查询支持。
与其他应用场景相比,这种架构对基于图技术的数据驱动应用具有特殊意义:历史数据形成的拓扑结构,可作为实时更新数据与 OLAP 输出(包括 KPI 指标、数据聚合结果、嵌入向量、社区发现等)的存储基础。该数据结构最终会被服务层查询遍历,此时图数据已通过前述处理过程实现了价值增强。

2.1 图处理引擎

为了选择合适的图处理引擎技术,关键要评估网络结构在内存中的占用规模与目标架构容量的匹配程度。在项目初期以快速构建最小可行产品 (Minimum Viable Product, MVP) 为目标时,可优先采用支持快速原型设计的简易框架。待性能与扩展性成为核心考量时,再替换为更先进的工具。采用微服务模块化设计并合理规划组件结构,可实现技术/库的独立替换以解决特定问题,这种架构思路也将影响后端技术栈的选型。
图处理引擎需要快速访问整个图的信息,也就是说,需要将整个图加载到内存中,且根据上下文,可能需要分布式架构。当处理合理规模的小型数据集时,networkx 是构建图处理引擎的理想库。面对仍可部署在单服务器或共享内存机器中的较大规模数据集,换用其他计算库能显著缩短处理时间。
然而,当数据集增长一定规模时,继续采用扩容共享内存机器(胖节点)的方案无论在技术还是经济层面都将难以为继。此时必须将数据分布到数十乃至数百个计算节点组成的集群中,实现横向扩展。在此类场景下,支持图处理引擎的两大主流框架分别是:

  • Apache Spark GraphX,作为 Spark 生态的图结构处理模块,它通过弹性分布式数据集 (Resilient Distributed Dataset, RDD) 实现顶点与边的分布式存储。图数据在计算节点间的划分可采用两种策略:逻辑上将节点分配到不同机器的边切割 (edge-cut),或逻辑上将边分配给不同机器同时允许顶点跨机器的顶点切割 (vertex-cut)。虽然基于 Scala 开发,但提供 PythonR 语言接口。内置 PageRank、连通组件、三角计数等算法,还可通过 SparklingGraph 等扩展库获取更多中心性度量指标
  • Apache Giraph,专为高扩展性设计的迭代式图处理系统,由 Facebook 开发并持续用于分析用户社交图谱。基于 Hadoop 生态系统构建,能充分释放海量结构化数据潜力。原生 Java 实现,同样提供 PageRank 和最短路径等基础算法的可扩展实现

当我们考虑扩展到分布式生态系统时,算法的选择要比在共享机器环境中少得多。这通常是由于两个原因:

  • 分布式算法实现复杂度因节点间通信大幅提升,整体效率随之降低
  • 更关键的是,大数据分析的核心准则要求算法(近似)线性扩展,只有随数据量增长通过增加计算节点即可保持性能的方案才值得实施

对此, GiraphGraphX 都支持基于 Pregel 标准接口定义可扩展的、以顶点为中心的迭代算法。Pregel 可被视为图数据结构上的迭代式 Map-Reduce 操作(实际上是对节点-边-节点三元组应用的迭代式 Map-Reduce 操作)。Pregel 计算由一系列称为"超步" (superstep) 的迭代组成,每个超步都涉及一个节点及其相邻节点。
在超步 S 中,系统会对每个顶点 V 应用用户自定义函数。该函数以超步 S-1 中发送给 V 的消息作为输入,并修改V及其出边的状态。这个函数代表可轻松并行化的映射阶段。除了计算 V 的新状态外,该函数还会向与 V 相连的其他顶点发送消息,这些顶点将在超步 S+1 接收这些信息。消息通常沿着出边发送,但也可以发送给任何已知标识符的顶点。在下图中,我们展示了 Pregel 算法在网络中计算最大值时的示意图。

超步S

借助 Pregel 框架,开发者能够以高效且通用的方式实现 PageRank、连通组件等算法,甚至能构建节点嵌入的并行化版本。

2.2 图查询层

随着非结构化数据的广泛普及,NoSQL 数据库开始获得前所未有的关注。其中,图数据库凭借其基于实体关系存储信息的独特优势展现出强大潜力。在众多应用场景中,数据天然适合被建模为携带元数据(节点属性)的实体,并通过附加关系属性(边属性)的边连接构成网络。
图数据库包括 Neo4jOrientDBArangoDBAmazon NeptuneCassandraJanusGraph 等库或工具。接下来,我们简要描述其中一些数据库,并解析用于查询和遍历底层图结构的专用查询语言,称为图查询语言。

3. Neo4j

Neo4j 是最常见的图数据库之一,拥有一个庞大的社区来支持其使用和推广。它有两个版本:

  • 社区版:以 GPL v3 许可证发布,允许用户/开发人员公开将 Neo4j 集成到他们的应用程序中
  • 企业版:专为商业部署设计,适用于规模和可用性至关重要的情况

Neo4j 通过分片技术实现横向扩展,即将数据分布到多个节点并行处理查询与聚合操作。其联邦查询功能还能将多个独立图(甚至采用不同模式的小型图)虚拟整合为统一图谱进行查询。该数据库的核心优势在于架构灵活性(支持模式演进)和操作便捷性,特别是其独创的 Cypher 查询语言——这种专为图数据库设计的声明式语言,可视为图数据库领域的 SQL
可以通过安装社区版,或在线体验,测试 Neo4jCypher。在线版预置了电影数据集等示例数据库,该数据集包含 38 部电影与 133 位参与者(演员/导演/编剧/制片人等关系)。无论是本地部署版还是在线版,都配备了可视化查询界面。例如执行以下 Cypher 查询即可获取电影数据集中的前 10 位演员::

MATCH (p: Person) RETURN p LIMIT 10

我们利用数据点之间的关联信息进行探索。数据库显示Keanu Reeves是其中的演员之一,我们可能想知道他在所列电影中与哪些演员合作过。通过以下查询即可轻松获取这些信息::

MATCH (k: Person {name:"Keanu Reeves"})- [:ACTED_IN]- (m: Movie)- [:ACTED_IN]- (a: Person) RETURN k, m, a

如下图所示,该查询通过直观的语法声明了我们感兴趣的路径,清晰地展示了如何遍历图。

示意图

除了 Cypher,还可使用 Gremlin 进行数据查询。Neo4j 还提供了与多个编程语言的绑定,如 PythonJavaScriptJavaGoSpring.NET。特别是对于 Python,有多个库实现了与 Neo4j 的连接,例如 neo4jpy2neoneomodel,其中 neo4j 是官方支持的库,与数据库的直接连接。创建连接并运行查询,查询可以是任何 Cypher 查询:

from neo4j import GraphDatabase
driver = GraphDatabase("bolt://localhost:7687", "my-user", "my-password")

def run_query(tx, query):
    return tx.run(query)

with driver.session() as session:
    session.write_transaction(run_query, query)

4. JanusGraph

Neo4j 凭借其直观的交互界面和查询语言,在快速实现需求时具有无可比拟的优势。它确实是适用于生产环境的图数据库,尤其在需要敏捷开发的 MVP 阶段表现突出。然而随着数据量增长,其基于分片技术、将大型图分解为若干子图的扩展方案可能并非最优选择。
当数据规模急剧膨胀时,开发者可能需要考虑其他图数据库方案。需要强调的是,这种考量应当仅在具体用例需求超出 Neo4j 扩展能力时进行——即当 MVP 初始需求发生演进升级的情况下。替代方案包括 Amazon NeptuneCassandra 等商业产品,也有 JanusGraph 等开源选项可用。
JanusGraph 是一款可扩展的图形数据库,旨在存储和查询分布在多机器集群中的图形数据,支持数百亿个顶点和边。事实上,JanusGraph 本身没有存储层,而是作为一个 Java 编写的组件,位于其他数据存储层之上,例如:

  • Google Cloud Bigtable:基于谷歌文件系统开发的专有数据存储云版本,专为跨数据中心的海量数据扩展而设计
  • Apache Cassandra:支持跨多数据中心部署的开源分布式 NoSQL 数据库,擅长处理超大规模数据集
  • ScyllaDB:专为实时应用优化的数据库,在保持与 Apache Cassandra 兼容的同时,显著提升吞吐量并降低延迟

通过这种架构设计,JanusGraph 继承了底层存储系统的核心优势——包括弹性扩展、高可用性和容错机制,并在其上层抽象出图数据模型接口。
通过与 ScyllaDB 的深度集成,JanusGraph 能够为需要极速响应、弹性扩展和高吞吐量的应用场景提供强力支持。此外,JanusGraph 还集成了基于 Apache LuceneApache SolrElasticsearch 的索引层,大幅提升了图数据内部的检索与搜索效率。
这种"分布式存储后端+智能索引层"的架构设计,使 JanusGraph 能够高效处理包含千亿级节点和边的超大规模图结构,特别是有效应对现实应用中常见的"超级节点"问题(即节点连接度极高的枢纽节点,这种现象符合 Barabasi-Albert 网络模型提出的"优先连接"理论,该模型揭示了真实网络中枢纽节点自然形成的规律)。
在大规模图数据场景中,超级节点往往成为性能瓶颈——尤其是当业务逻辑需要以这些节点为枢纽进行图遍历时。JanusGraph 通过支持快速过滤相关边的属性配置,能显著提升遍历效率,实现更优的系统性能。
通过集成 Apache TinkerPop 框架JanusGraph 提供了标准的图查询与遍历 API。这个开源且厂商中立的图计算框架,采用 Gremlin 图遍历语言作为统一查询接口。所有兼容 TinkerPop 的图数据库都能实现无缝互操作,使开发者可以构建不依赖特定后端技术的"标准化"服务层,根据实际需求自由选择或切换图数据库技术栈。事实上,包括 Neo4j 在内,目前主流图数据库都已支持 TinkerPop 集成。
Java 连接器外,通过 gremlinpython 库提供的 Python 原生绑定,开发者可以直接使用 Python 应用程序连接和遍历图数据库。进行图结构查询前,首先需要建立如下数据库连接:

from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
connection = DriverRemoteConnection(
    'ws://localhost:8182/gremlin', 'g'
)

建立连接后,我们需要实例化 GraphTraversalSource 对象——这是所有 Gremlin 图遍历操作的基础组件,并将其绑定到已创建的数据库连接:

from gremlin_python.structure.graph import Graph
from gremlin_python.process.graph_traversal import __
graph = Graph()
g = graph.traversal().withRemote(connection)

实例化 GraphTraversalSource 后,即可在应用程序中复用该对象进行图数据库查询:

co_actors = g.V().has('Person', 'name', 'Keanu Reeves').out("ACTED_IN").in("ACTED_IN").values("name")

从上述代码可以看出,Gremlin 是一种函数式查询语言,通过操作符链式组合形成路径式表达式。

5. Neo4j vs GraphX

Neo4jGraphX 是针对不同需求场景设计的:Neo4j 专注于图结构数据存储与实时查询,而 GraphX 擅长大规模图数据分析处理。虽然理论上可以将 Neo4j 作为处理引擎使用(其生态中的 Graph Data Science 库确实具备处理能力),或者将 GraphX 作为内存图数据库使用,但这类做法并不推荐。
图处理引擎通常负责计算关键绩效指标,这些指标会被存储在图数据库层(通常会建立索引以提高查询和排序效率)以供后续使用。因此,像 GraphX 这样的技术与 Neo4j 等图数据库并非竞争关系,它们完全可以在同一应用中协同共存,各自服务于不同目的。即便在最小可行产品和早期阶段,最佳实践也是将图处理引擎与图查询引擎这两个组件分离,并分别为其选用合适的技术方案。

小结

在本节中,介绍了设计、实现和部署基于图建模与图结构的数据驱动应用程序的基本概念。强调了模块化方法的重要性,这通常是将数据驱动用例从早期最小可行产品 (Minimum Viable Product, MVP) 无缝扩展至能处理海量数据与高计算性能的生产系统的关键所在。
我们概述了主要的架构模式,为设计数据驱动应用程序的骨干结构提供指导。接着,描述了图形驱动应用程序的主要组件:图处理引擎、图数据库和图查询语言。针对每个组件,我们不仅介绍了最常用的工具和库,并通过实践示例构建和实现解决方案。


网站公告

今日签到

点亮在社区的每一天
去签到