1. 数据导向架构:为什么它是机器学习系统的“解药”?

如果你正在构建或维护一个机器学习系统,大概率遇到过这些头疼事:模型训练好了,但上线后数据管道一塌糊涂,各个服务间数据格式对不上;想加个新特征,结果要改七八个微服务,牵一发而动全身;系统监控像盲人摸象,数据流到哪了、模型为什么漂移,全靠猜。这些问题,根源往往不在算法本身,而在于系统架构——数据没有被当作一等公民来对待。

数据导向架构(Data-Oriented Architecture, DOA)正是为解决这些问题而生的设计范式。它不是什么全新的魔法,而是一种思维转变:从传统的“服务调用服务”(Service-Oriented Architecture, SOA)或“函数调用函数”的模式,转向“服务读写共享数据”的模式。想象一下,你的系统不是一个由紧密咬合的齿轮组成的精密钟表(一个齿轮坏了,整个表可能就停了),而更像一个繁忙的物流中心。数据是包裹,各个处理组件(分拣、打包、运输)是独立的工作站。它们不直接互相喊话“下一个包裹给我”,而是都从传送带(共享数据流)上取包裹,处理完再放回传送带。这样,增加一个“质检工作站”或调整“分拣规则”,都不会要求其他工作站停工改造。

在机器学习系统的语境下,DOA的价值被急剧放大。因为ML系统本质就是数据驱动的:从数据收集、特征工程、模型训练、在线推理到效果监控,每一个环节都在生产、消费和转换数据。DOA的核心原则——数据作为一等公民、优先去中心化、保持开放性——恰好能系统性地应对ML系统在可扩展性、灵活性和可维护性上的核心挑战。接下来,我将结合大量一线系统的设计案例,拆解DOA的三大原则在ML系统中是如何落地、为何有效,以及实践中会遇到哪些“坑”。

2. DOA核心原则在ML系统中的深度解析

2.1 原则一:数据作为一等公民——不止是口号

“数据作为一等公民”听起来很抽象,但在工程上,它具体体现在两个可落地的子原则上: 共享数据模型 数据耦合

2.1.1 共享数据模型:定义系统的“通用语言”

共享数据模型,指的是系统中多个组件对同一种数据,有统一的理解和表达格式。这不仅仅是定义一个Protobuf或Avro Schema那么简单。在ML系统中,它至少包含三个层次:

  1. 特征定义层 :一个特征“用户近30天点击率”,其计算口径、取值范围、缺失值处理方式,在特征仓库、训练样本生成、在线推理服务中必须完全一致。
  2. 样本/事件层 :一条用于模型训练或推理的样本,其结构(如 {user_id, item_id, context_features, label} )在所有读写它的组件间是共识。
  3. 模型元数据层 :模型的版本、输入输出签名、性能指标、依赖的特征列表等,需要有一个统一的注册和发现机制。

为什么这如此重要?我见过一个推荐系统,特征工程用Python字典,训练用的是TFRecord,线上推理却是JSON。每次特征迭代,三个地方要同步改,一旦漏掉一处,线上线下的数据分布就偏了,模型效果莫名其妙下跌,查因就要好几天。而采用共享数据模型(例如,所有环节都通过一个中央特征库定义的Protobuf Schema来序列化数据),就能从根本上杜绝这种不一致。

在实际系统中,共享数据模型主要通过两种技术载体实现: 数据流 数据库/数据湖

  • 数据流(如Apache Kafka, Pulsar) :适用于实时、连续的数据管道。例如,一个实时反欺诈系统,用户行为事件作为 UserAction 消息写入Kafka Topic。特征计算服务消费这些消息,生成实时特征 RealTimeFeatures 写入另一个Topic。模型推理服务再消费 RealTimeFeatures ,输出 RiskScore 。所有服务都对这几个消息的Schema有共同约定。这种模式的优点是低延迟和解耦,组件可以独立扩缩容。
  • 数据库/数据湖(如AWS S3, HDFS, 分布式数据库) :适用于批处理、历史数据分析或需要强一致性的场景。例如,一个用户画像系统,将清洗后的用户标签定期以Parquet格式写入数据湖(S3)。离线训练任务、在线画像服务、数据分析报表都从同一个S3路径读取这些Parquet文件。这保证了所有下游任务看到的是同一份“真相之源”。

实操心得:Schema演化是必考题 共享数据模型不是一成不变的。业务在变,特征在增删改。因此,必须从一开始就设计好Schema的演化策略。对于使用Avro或Protobuf的系统,要明确兼容性规则(如只增不减字段、不改字段类型)。对于Kafka,要配合Schema Registry使用。一个血泪教训:在没有Schema Registry的情况下,生产者更新了消息格式但没通知所有消费者,导致线上服务大面积反序列化失败。

2.1.2 数据耦合:让组件通过数据“对话”,而非直接“喊话”

数据耦合是“数据作为一等公民”原则在通信层面的体现。它要求组件之间不通过直接的API调用(如REST、gRPC)来传递数据和触发行为,而是通过读写共享的数据媒介(即上文的数据流或数据库)来间接通信。

传统服务调用模式(A调用B)像是打电话:A必须知道B的电话号码(地址),并且B必须在电话旁(可用),通话是同步的,一方忙线另一方就得等待。而数据耦合模式像是留言板:A把需求写在公共留言板(如Kafka Topic)上,B有空的时候就去查看并处理。A不关心B是谁、在哪、何时处理。

在ML系统中,数据耦合的优势极为明显:

  • 解耦与弹性 :特征计算服务挂了,不会阻塞事件采集服务写入数据;模型服务升级重启时,推理请求会在消息队列中堆积,而不会直接超时失败。
  • 回溯与调试 :所有流经系统的数据都有记录(消息日志或数据库表),当模型预测出现异常时,可以完整地回溯到当时模型“看到”的输入数据,复现问题。
  • 支持多种消费速度 :离线训练任务可以以天为单位批量消费历史数据,而在线监控服务可能需要实时消费同一份数据流。数据耦合让两者互不干扰。

一个典型的采用数据耦合的ML系统架构如下:数据源 -> Kafka (原始事件) -> 流处理服务 (特征计算) -> Kafka (特征事件) -> 模型推理服务 -> Kafka (预测结果) -> 监控/业务服务。每个箭头都代表数据写入和读取,而非服务调用。

注意事项:数据耦合的代价 数据耦合并非银弹。它引入了新的复杂性:

  1. 数据一致性 :从“最终一致性”变成了“最终一致性+”。你需要考虑消息的传递保证(至少一次、恰好一次、至多一次),以及跨多个数据媒介的状态一致性如何保障。
  2. 系统可观测性 :链路变长了,一个业务请求的完成涉及多个异步的数据读写步骤。传统的基于请求ID的链路追踪(Tracing)需要改造,以支持跨消息的追踪。
  3. 开发心智负担 :开发者从“调用函数-获得结果”的同步思维,需要转变为“发布事件-监听结果”的异步思维,调试和测试的复杂度都会增加。

2.2 原则二:优先去中心化——应对数据洪流与隐私挑战

去中心化原则包含三个层面: 本地数据块 本地优先 点对点优先 。对于ML系统,这直接关系到如何处理海量、分布式的数据源,以及如何满足低延迟和隐私合规要求。

2.2.1 本地数据块与本地优先:让计算贴近数据

在边缘计算和物联网(IoT)场景中,数据产生于终端设备(手机、传感器、摄像头)。将所有这些原始数据全部上传到云端中心处理,既不经济(带宽成本),也不现实(延迟太高),更不安全(隐私数据暴露)。

“本地数据块”是指数据在产生它的设备或最近的边缘节点上进行存储和初步处理。“本地优先”则是指计算任务(特别是模型推理)尽可能在数据源头完成。例如:

  • 智能手机上的输入法预测 :模型直接在手机端运行,你的输入数据无需离开设备。
  • 工厂质检摄像头 :视觉缺陷检测模型部署在车间边缘服务器上,实时分析视频流,只有报警图片和元数据才上传云端。
  • 联邦学习 :模型训练直接在各个医院的数据中心进行,只交换加密的模型参数更新,原始病历数据永不离开本地。

这种模式的优势是显而易见的: 低延迟 (响应快)、 带宽节省 隐私保护 (原始数据不出域)、 可靠性高 (网络中断不影响本地核心功能)。

2.2.2 点对点优先:构建无单点瓶颈的协作网络

“点对点优先”比“本地优先”更进一步,它强调节点之间直接通信和协作,而不是通过一个中心节点来中转。这在需要大规模并行计算或构建抗审查系统时非常有用。

在ML系统中,一个典型的应用是 分布式训练 。在参数服务器架构中,还存在中心节点(Parameter Server)的瓶颈。而在更先进的点对点同步训练(如All-Reduce算法)中,每个工作节点(Worker)都与其他节点直接通信,交换梯度信息,共同完成模型更新。这消除了中心节点的单点故障和通信瓶颈,实现了更高的扩展性。

另一个例子是 去中心化的内容推荐/搜索 。想象一个没有中心化搜索引擎的网络,每个节点(用户客户端或服务器)既存储部分内容索引,也具备简单的排序模型。查询请求在节点间路由,最终通过协作返回结果。这虽然目前还不是主流,但在特定隐私敏感或抗审查场景下有探索价值。

实操心得:去中心化带来的新挑战

  1. 节点管理 :成百上千的边缘节点,如何统一部署、配置、监控、更新模型?你需要强大的设备管理平台和OTA(空中下载)更新能力。
  2. 数据与模型版本一致性 :如何确保所有边缘节点上的特征处理逻辑和模型版本是一致的?不一致会导致“同一数据,不同结果”的混乱。这需要设计严谨的版本控制和发布流程。
  3. 协调与共识 :在点对点网络中,如何达成共识(例如,哪个模型版本是最好的)?这需要引入分布式共识算法(如Raft、Paxos),增加了系统复杂度。

2.3 原则三:开放性——构建可进化、可观测的系统

开放性原则要求系统由 自治的实体 组成,它们通过 异步通信 和标准的 消息交换协议 进行交互。这听起来很理论,但在ML系统的运维中,它是实现系统可进化性和可观测性的基石。

2.3.1 自治实体:每个组件都是“微服务+”

自治实体意味着系统的每个组件(数据采集、特征管道、模型服务、监控告警)都可以独立开发、部署、伸缩和失败,而不影响其他组件。这比微服务概念更进一步,强调组件之间 仅通过共享数据模型进行通信 ,彻底杜绝了隐式的、紧耦合的依赖。

例如,模型服务不需要知道特征服务的主机名和端口,它只需要订阅特定的Kafka Topic,读取符合约定Schema的特征数据。这样,特征服务可以用Java重写并部署十倍实例,模型服务完全无感知、无需改动。

2.3.2 异步通信与标准协议:松耦合的保障

异步通信是自治实体能够独立运作的前提。同步调用意味着调用方会被被调用方阻塞,它们的可用性绑定在一起。而异步通信(通过消息队列)将时间上的耦合也解除了。

标准化的消息交换协议(如CloudEvents)则确保了不同团队、甚至不同公司开发的组件能够无缝集成。在ML系统中,可以定义诸如 ModelTrainingRequested FeatureSetUpdated ModelDeployed PredictionDriftDetected 等标准事件。监控组件监听所有事件,就能构建出整个系统数据流和生命周期的全景视图,实现真正的可观测性。

3. 现状调研:DOA原则在真实ML系统中的采纳度

为了客观了解DOA在业界的实践情况,我们参考了一项对45个已部署的ML系统研究论文的系统性综述。该综述从“数据作为一等公民”、“优先去中心化”、“开放性”三个维度,评估了这些系统对DOA原则的遵循程度。下表概括了核心发现:

DOA原则 子原则 完全采纳 部分采纳 未采纳 典型场景与原因
数据作为一等公民 数据驱动 100% 0% 0% ML系统天生依赖数据,所有系统均满足。
共享数据模型 60% 13.3% 26.7% 采纳 :需处理实时流(Kafka)或历史批数据(数据库)。 部分 :混合存储(如DB+流)。 未采纳 :简单系统或紧密耦合的云微服务(通过API隐藏数据)。
数据耦合 33.3% 15.6% 51.1% 采纳 :实时系统、大数据批处理。 部分 :核心组件间用数据媒介,与外部系统用API。 未采纳 :多数仍使用REST/gRPC进行组件间通信。
优先去中心化 本地数据块 17.8% 17.8% 64.4% 采纳 :SETI天文数据处理、社交媒体分析等需处理海量分散数据。 部分 :联邦学习、边缘计算(数据本地预处理)。 未采纳 :多数采用中心化云存储。
本地优先 17.8% 31.1% 51.1% 采纳 :低延迟、高吞吐需求(如Spark分布式计算)。 部分 :云边协同(边缘推理,云端训练)。 未采纳 :计算集中部署在云端或单一设备。
点对点优先 20.0% 0% 80.0% 采纳 :极少,用于需节点直接协作的分布式计算(如All-Reduce)。绝大多数系统存在中心协调节点。
开放性 自治实体 较低 - - 多数系统组件仍存在较强依赖,完全自治的案例较少。
异步通信 常见 - - 在采用数据流或消息队列的系统中普遍实现。
标准消息协议 极少 - - 系统内部多使用特定技术(如Kafka协议),跨系统标准协议(如CloudEvents)应用不广。

核心发现解读:

  1. “数据驱动”已成共识,但“数据耦合”尚未普及 :几乎所有系统都承认数据的重要性,但超过一半的系统仍采用传统的同步API调用(REST/gRPC)作为组件间主要的通信方式。这表明,从“数据意识”到“数据架构”的转变仍在进行中。许多团队优先考虑开发的便利性(同步调用更直观),而暂时接受了由此带来的耦合风险。
  2. 中心化部署仍是主流,去中心化多为特定需求服务 :超过60%的系统采用中心化的云存储和计算。去中心化设计(边缘计算、分布式处理)主要是为了满足 低延迟 (工业实时控制、自动驾驶)、 数据隐私 (医疗、金融)、 带宽限制 (物联网)或 极端算力需求 (大规模科学计算)等特定约束,而非架构首选。
  3. 开放性处于初级阶段 :虽然异步通信在数据流架构中很常见,但系统的“开放性”更多体现在技术栈内部(如都用Kafka)。真正的、基于标准协议的、高度自治的组件化系统仍属前沿探索。这背后是工具链、设计方法论和运维经验的缺失。

4. 实施DOA的典型架构模式与实操要点

基于上述原则和现状,我们可以勾勒出几种在ML系统中实践DOA的典型架构模式。

4.1 模式一:中心化数据流架构(最易落地)

这是目前最成熟、应用最广的DOA实践模式。系统拥有一个逻辑上中心化的、统一的数据流总线(如Apache Kafka集群),所有组件都围绕这个总线进行建设。

  • 架构示意图
    [数据源] -> (注入) -> [Kafka Cluster] <- (消费) <- [流处理/特征工程]
                                                              |
                                                              v
                                                    [Kafka (特征Topic)] <- (消费) <- [模型推理服务]
                                                                              |
                                                                              v
                                                                     [Kafka (预测Topic)] <- (消费) <- [业务应用/监控]
    
  • 核心特征
    • 共享数据模型 :所有流经Kafka的消息都有严格定义的Avro/Protobuf Schema,并在Schema Registry中管理。
    • 数据耦合 :组件间仅通过Kafka Topic交互,无直接服务调用。
    • 异步通信 :天然异步。
    • 部分开放性 :组件可独立开发部署,但依赖中心化的Kafka集群。
  • 适用场景 :实时推荐、风控、监控告警等对实时性要求高、数据管道复杂的在线ML系统。
  • 实操要点
    • Topic规划 :按数据域和变更频率划分Topic。例如, user_behavior_events , item_features , model_predictions 。避免一个Topic承载多种语义的数据。
    • 消费者组管理 :合理利用消费者组实现“广播”(不同业务消费相同数据)和“负载均衡”(同一业务多个实例并行消费)。
    • 延迟与吞吐权衡 :根据业务需求调整Kafka的 acks linger.ms batch.size 等参数。追求低延迟可能牺牲吞吐量,反之亦然。

4.2 模式二:云边协同分层架构(应对去中心化需求)

这种模式将系统划分为云、边、端多个层次,在不同层级应用不同的DOA原则。

  • 架构示意图
            [云端中心]
    (模型训练/管理, 全局数据聚合)
                ^
                | (同步模型参数, 上传聚合数据)
                |
            [边缘节点集群] (如工厂网关、区域服务器)
    (本地数据存储, 实时推理, 数据预处理)
                ^
                | (收集原始数据, 下发模型)
                |
            [终端设备] (摄像头、传感器、手机)
        (数据采集, 轻量级推理)
    
  • 核心特征
    • 本地数据块/本地优先 :原始数据在边缘或终端处理,敏感数据不出本地。
    • 分层共享数据模型 :云端定义全局特征和模型标准,边缘可根据本地情况做适配,但需保证与云端交互的接口一致性。
    • 混合通信 :边缘内部可能采用数据流,云边之间可能采用同步API(用于控制)和异步消息(用于数据上报)结合。
  • 适用场景 :智慧工厂、智能驾驶、智慧医疗、联邦学习等。
  • 实操要点
    • 模型轻量化与适配 :需要为边缘设备提供剪枝、量化、蒸馏后的轻量模型,并考虑不同硬件的推理引擎(TensorRT, OpenVINO, Core ML等)。
    • 离线与降级能力 :边缘节点必须具备在网络中断时独立运行的能力。这意味着本地需要有足够的历史数据或基础模型进行推理。
    • 版本协同 :模型在云端更新后,如何安全、灰度地推送到成千上万的边缘节点,并确保回滚机制,是巨大的运维挑战。

4.3 模式三:数据湖仓一体架构(面向分析与训练)

这种模式以数据湖/仓(如Delta Lake, Apache Iceberg + 查询引擎)作为核心的共享数据模型,批处理和流处理都向其中读写数据。

  • 架构示意图
    [流数据] -> (Kafka) -> [流式摄入] -> [Data Lakehouse]
    [批数据] -> (ETL) ---^                     |
                                                              | (SQL/DataFrame)
                                                              v
                                            [特征工程] [模型训练] [数据分析]
    
  • 核心特征
    • 强共享数据模型 :所有数据,无论实时还是离线,都以开放的格式(Parquet, ORC)存储在数据湖仓中,并具有统一的元数据管理。
    • 数据耦合 :训练任务、特征作业、分析查询都直接读取湖仓中的数据,彼此独立。
    • 支持回溯与一致性 :湖仓的ACID事务和时间旅行功能,完美支持数据版本化和模型训练的可复现性。
  • 适用场景 :对训练数据一致性、实验可复现性、历史数据分析有强需求的复杂ML系统。
  • 实操要点
    • 数据治理是生命线 :必须建立严格的数据血缘、质量监控和生命周期管理。否则数据湖极易沦为“数据沼泽”。
    • 统一计算引擎 :考虑使用Spark、Flink或新兴的StarRocks等引擎,同时处理批、流查询,减少技术栈复杂度。
    • 成本控制 :湖仓存储成本低,但计算成本可能很高。需要精细化的资源管理和查询优化。

5. 挑战、陷阱与未来方向

尽管DOA前景广阔,但在ML系统中全面落地仍面临显著挑战。

5.1 认知与技能挑战 最大的障碍来自团队。开发者习惯了面向对象和微服务的设计模式,转向以数据流和数据模型为中心的思维需要时间。运维人员需要掌握消息队列、流处理平台、分布式存储等新基础设施的运维技能。这需要系统的培训和文化建设。

5.2 工具链与生态不成熟 成熟的SOA有Spring Cloud、Dubbo等全套生态。DOA在ML领域的工具链仍在拼图中。虽然Kafka、Flink、Iceberg等底层设施很强大,但缺少更高层次的、开箱即用的“MLOps on DOA”框架,用于快速编排特征管道、模型服务、实验跟踪等组件。许多公司需要投入大量工程力量自建平台。

5.3 数据治理与可观测性复杂度激增 当数据在系统内自由流动时,回答“这个数据从哪来?”“谁修改了它?”“当前模型用了哪个版本的特征?”变得异常困难。需要构建强大的 数据血缘系统 特征版本库 跨组件的分布式追踪体系 。这部分的投入往往被低估。

5.4 一致性与正确性更难保证 在异步、事件驱动的系统中,实现“恰好一次”处理语义、保证跨多个数据源的状态一致性,比在同步系统中复杂得多。需要仔细设计幂等性、使用CDC(变更数据捕获)工具、甚至引入事件溯源(Event Sourcing)模式。

未来方向

  1. 标准化 :出现更多像CloudEvents、CDEvents这样针对ML流水线的事件标准,降低组件集成成本。
  2. 智能化 :基于DOA架构,构建更智能的自动化运维系统,例如自动检测数据分布漂移并触发模型重训练,自动优化数据流图的拓扑结构。
  3. 融合 :DOA不会完全取代SOA或微服务。未来的趋势是 混合架构 :系统内部核心的、数据密集的ML管道采用DOA,而对外的、业务逻辑复杂的用户接口则采用成熟的微服务。关键在于清晰地定义边界,并通过事件(数据)进行桥接。

从我个人的实践经验来看,向DOA转型不是一蹴而就的“大爆炸”式改革。更可行的路径是 渐进式演进 :从一个最痛苦、最复杂的ML数据管道开始(比如实时推荐的特征工程链路),将其改造成基于数据流和数据模型的独立子系统。让团队在这个“样板间”里积累经验、验证价值、打磨工具。当收益显现、信心建立后,再将此模式推广到其他子系统。记住,架构的终极目标是服务于业务和团队,选择DOA不是为了追求时髦,而是因为它确实是解决ML系统固有复杂性的一剂良方。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐