通用数据架构思考(一):从 Edge 到 Cloud
我之前做过一段时间机器人云平台。那时平台处理的主要还是 metrics:设备上报状态、资源使用率、心跳和告警,再把这些数据汇总起来用于监控和运维。它已经让我接触到设备接入、事件流、时序查询和可靠传输这些问题,但和今天常见的多模态大数据相比,确实只能算小巫见大巫。
现在的数据源不只是一串数值。机器人、车辆、工业设备、摄像头、IoT 终端,甚至手机上的多媒体应用,会持续产生视频、图片、点云、音频、状态、事件、日志和指标。其中有些记录只有几十字节,有些一个文件就是几个 GB;更重要的是,它们还需要按时间、帧和场景关联起来,才能解释当时到底发生了什么。
所以我想沿着以前的 metrics 数据架构继续往外延伸:当数据变成多模态、高吞吐、带有强上下文的大对象时,采集、传输、存储和处理应该怎样重新划分边界?如何把这些原始数据进一步筛选、加工成可复用的数据资产,并让结果反过来改进下一轮生产系统?
这是「通用数据架构思考」系列的第一篇。我把这条架构演进路径称为“多模态数据闭环”:它不是某一种模型或某一个行业的专属架构,而是一组可复用的分布式数据系统原则。下一篇会从对象进入 READY 后继续往下,讨论转码、推理、质检等数据处理任务如何被可靠地调度。

为了让后面的抽象不至于飘在空中,先固定一个贯穿全文的例子:一个部署在现场、带有相机和传感器的终端检测到异常。它从 ring buffer 中截取异常前后的图片、视频和传感器片段,创建上传会话后直传对象存储;云端校验完成,将对象登记为 READY,再通过事件流触发转码、质量检查和场景索引。最后,高价值样本进入数据集,用于分析、训练或规则迭代,并把结果反馈到下一次现场运行。机器人、车辆、工业设备和摄像头的差异,主要体现在数据语义,而不是这条主干。
数据闭环:生产数据不是终点
把模型或规则部署到生产环境,通常不是数据生命周期的终点,而是下一轮数据的开始。
例如,一个视觉模型会遇到训练集中没有覆盖好的光照、材质、障碍物或设备状态;车辆可能出现低置信度预测和人工接管;工业设备会出现罕见的振动模式;客户端也会暴露出新机型、新网络条件下的异常。它们都是应该被记录、筛选和回流的信号。
完整过程可以抽象为:
生产环境 / 数据源
│
▼
采集与选择
│
▼
存储与处理
│
▼
筛选、标注、建集
│
▼
训练、分析或规则迭代
│
▼
评估与部署
│
└────────────→ 产生下一轮数据
核心逻辑很简单:生产环境产生数据,数据改善决策或模型,新的决策或模型重新进入生产环境。
这里的“下游”不一定是机器学习训练。它也可以是 BI 分析、故障诊断、质量追溯、监控告警或控制策略。闭环的重点不是用了 AI,而是结果能够被追溯到原始数据,并能影响下一次行动。
多模态数据为什么麻烦
传统业务数据往往以事务和表记录为中心;多模态数据则天然异构。以一个带视觉和传感器的终端为例,可能同时产生:
视觉:图片、视频
空间:点云、定位信息
声音:音频流
状态:传感器读数、设备状态、控制信号
模型:预测结果、置信度、版本号
系统:日志、CPU / GPU / 内存指标
它们的体积、保留时间、上传时机和查询方式都不同。视频和点云更适合对象存储;设备状态可能只需要关系数据库中的一条记录;高频指标适合时序系统;事件则可能需要消息流来解耦消费方。
更难的是关联关系。一个“场景”通常不是一份孤立文件,而是同一时间窗口里的多个信号:
timestamp T
│
┌───────────┼───────────┐
▼ ▼ ▼
Camera Sensor Runtime
│ │ │
└───────────┼───────────┘
▼
Frame
▼
Scene
所以后端并不需要理解每一种算法细节,但必须为设备、传感器、帧、场景和模型版本建立稳定的关联与索引。时间也不应只保存一个 timestamp:至少要分清采集时间与入库时间,并记录时钟来源、偏差/不确定性、标定版本和坐标系。否则跨传感器对齐可能看似正确,实际上已经漂移;文件虽然存下来了,之后却找不回“当时发生了什么”。
Edge 不该只是上传器
假设有 10,000 个数据源,每个每天产生 100 GB 原始数据,那么总量就是约 1 PB/天。这只是量级示例;真实成本还取决于压缩率、留存期和现场上行带宽。无差别把原始数据上传云端,不仅贵,也会让网络、存储和后续计算都忙于处理低价值数据。
因此,靠近数据源的 Edge Agent 应当承担第一层数据处理:
数据源
│
▼
┌──────────────────────┐
│ Edge Data Agent │
│ collect │
│ preprocess │
│ filter / sample │
│ compress │
│ local buffer │
│ select / upload │
└──────────┬───────────┘
▼
Cloud
边缘侧可以对不同数据做不同处理:视频降采样和压缩,传感器数据聚合,日志去重,指标按窗口汇总。原则是:尽量在靠近数据源的位置减少无价值数据,但不要丢掉未来排障或训练真正需要的上下文。
一个很常见的实现是 ring buffer。Edge 持续保留最近一段时间的数据;当低置信度、异常告警、人工介入或业务规则触发时,再截取触发前后的窗口上传:
Edge Ring Buffer
-30s ─────── NOW ─────── +10s
│
Trigger
│
▼
选择数据窗口并上传
它把“持续产生的大量普通数据”转化为“带上下文的高价值样本”。这就是数据选择的第一层,也往往比云端事后处理便宜得多。
控制面和数据面要分开
大文件上传时,最容易犯的错误是让业务 API 充当代理:
Edge ── 5 GB 文件 ──→ Backend ── 5 GB 文件 ──→ Object Storage
Backend 在这里没有产生业务价值,只是转发字节,却消耗了连接、内存、带宽和扩容资源。更合适的路径是分离控制面与数据面:
Control Plane
Edge ── 创建上传会话 ──→ API
Edge ←─ 上传策略 / 临时凭证 ─ API
Data Plane
Edge ═══════════════════════→ Object Storage
GB / TB 数据
控制面处理认证、授权、存储选择、上传会话、元数据和策略;数据面让客户端直接向对象存储传输大对象。设备只需要知道稳定的控制面入口,而不应硬编码某个 bucket 或物理存储地址。这样可以按数据类型、地区、租户或成本策略路由到不同存储,底层迁移时也不需要升级所有 Edge。预签名 URL 也应只授权某个会话对应的 object key、方法、长度/类型、校验和与短暂有效期,而不是给设备一个宽泛的 bucket 写权限。
对于大对象,上传协议还需要支持 multipart、并发、断点续传和按分片重试:
20 GB Object → Part 1 ✓ Part 2 ✓ Part 3 ✗ Part 4 ✓
│
└── 只重试 Part 3
网络经常在移动、弱网或离线之间切换。可靠上传并不是“HTTP 请求成功一次”,而是 Edge 侧有本地磁盘缓冲和持久化队列,能够在网络恢复后继续上传:
PENDING → UPLOADING → UPLOADED
│
└────→ RETRY → resume
从这个意义上说,Edge Agent 本身就是一个小型、可靠的数据系统。
多模态预处理:不是把文件转个格式就结束
metrics 的预处理通常比较直接:校验字段、按时间窗口聚合、补齐标签,然后写入时序系统。多模态数据的“可用”门槛高得多。一个视频、点云或音频文件成功上传,并不意味着它已经能被可靠地检索、对齐或用于后续分析。
预处理往往包括几类工作:
时空对齐:采集时间与入库时间、时钟源/偏差、frame 对齐、坐标系和传感器标定信息
格式规范:转码、分片、压缩、统一编码与 schema
质量检查:损坏文件、黑帧、丢帧、模糊、传感器漂移、缺失模态
上下文补全:设备、地点、软件版本、模型版本、触发事件与场景 ID
派生结果:缩略图、关键帧、波形、特征、预览和可检索索引
安全与合规:脱敏、遮挡敏感区域、保留策略和访问标签
其中最容易被忽略的是“关联信息”。一张图片如果没有拍摄时间、相机内外参、设备版本和同一场景下其他传感器的引用,往往只是一张孤立文件;把这些信息写成可查询的 metadata,才让它成为可复用的数据样本。实践中可以把这份信息收成一个不可变 manifest:其中包含 schema version、模态与编码、标定/模型版本、处理器版本和输入对象引用;演进 schema 时,兼容策略也应随 manifest 一起定义。
预处理放在 Edge 还是云端?
这不是二选一,而是延迟、带宽、算力和可回放性之间的取舍。
| 更适合 Edge | 更适合 Cloud |
|---|---|
| 必须实时完成的过滤、触发和 ring buffer 截取 | 需要全局视角的去重、场景挖掘与跨设备关联 |
| 为节省带宽而做的采样、压缩、轻量质量判断 | GPU 密集的转码、特征提取、模型推理和索引构建 |
| 依赖设备现场上下文的时间戳、标定和事件封装 | 可随算法升级重新运行的处理与数据回填 |
| 数据出域前必须完成的脱敏 | 需要统一规范、统一版本管理的 schema 与质量规则 |
Edge 的优势是离数据最近,能够立刻减少无价值传输,并在网络离线时保留必要上下文;代价是设备算力、磁盘和软件升级都有限,而且一旦只上传“处理后的结果”,未来可能失去重新处理原始数据的机会。云端的优势是算力集中、规则统一、可以随着算法变化反复处理;代价则是先要承担传输和存储原始数据的成本。
因此更常见的是分层方案:Edge 做不可等待、必须省带宽或必须在现场完成的轻量处理;云端保留足以重放的原始数据或高保真片段,再做耗时且会持续演进的重处理。每个派生结果都应该记录处理器版本、输入对象和参数,避免以后无法解释“这个缩略图或特征是怎么来的”。
“保留原始数据”也不是默认答案。是否允许原始数据出域,应由数据分类、地域、用户同意与处理目的决定;传输与静态存储需要加密和密钥管理,保留与删除策略则要同时覆盖原件、派生物、索引和备份,并留下访问审计记录。
不同数据,走不同的入口
把所有数据塞进同一条 ingestion pipeline 通常会很快变得别扭。一个温度读数可能只有几十字节,一段视频则有数 GB;两者在吞吐、延迟、查询和保存成本上的要求完全不同。
Edge Agent
│
┌──────────────┼──────────────────┐
▼ ▼ ▼
Heartbeat / Telemetry Events Large Objects
│ │ │
▼ ▼ ▼
Redis / TSDB Kafka / MQ Object Storage
这不要求每个项目一开始就引入完整的大数据组件。重点是按数据特征划分边界:高频指标采用适合聚合和查询的通道;状态变化和业务事件用消息流解耦;图片、视频、点云等 payload 用对象存储;数据库主要承担元数据和业务状态。
一个很实用的划分是:
Object Storage:保存大对象 payload
Metadata DB: 保存对象描述、索引和状态
例如元数据表记录 object_id、source_id、采集时间、sensor_type、逻辑 locator、size、内容校验和、scene_id 和处理状态;真正的字节则在对象存储里。对外暴露稳定的 object_id,由控制面解析当前存储位置并签发访问地址,比让下游依赖物理 bucket/key 更利于迁移。数据库负责回答“某个场景有哪些数据、是否可用”,不应承担保存视频本体的工作。
上传完成不是一个瞬间,而是状态机
对象存储和关系数据库不是同一个事务系统,因此不要假设下面的伪事务真的存在:
BEGIN;
INSERT metadata;
PUT object;
COMMIT;
现实中可能出现对象已经上传、元数据还没写入,或元数据已创建、对象最终没有上传成功。一个更可靠的上传工作流是显式维护状态:
CREATED → UPLOADING → VERIFYING → READY
│
└──────→ FAILED
典型过程如下:
- Edge 请求创建上传会话,控制面完成认证、授权、存储选择并创建元数据。
- 服务端返回临时上传凭证或预签名 URL。
- Edge 直传对象存储,并按需要分片续传。
- Edge 调用完成接口,服务端验证对象 key 与会话匹配、multipart completion、大小和内容 checksum,再将其标记为
READY。
这里的 checksum 应是协议明确传递并校验的内容摘要,例如 SHA-256 或对象存储提供的 checksum 字段;不要把 multipart 上传中的 ETag 当作通用内容 hash。失败、超时和客户端崩溃都不可避免。状态机配合幂等请求、重试任务和定期 reconciliation,才是跨多个独立系统获得最终一致性的方式。还要扫描过期会话和孤儿对象,并为未完成的 multipart upload 配置生命周期 abort,避免零散分片持续计费。重点不是强求一次操作全成功,而是保证系统最终能够发现并修复不一致。
云端处理:从原始数据到可用数据集
Edge 选择只能完成第一层筛选。数据进入云端后,往往还要经过异步处理:解码、格式转换、质量校验、去重、场景挖掘、特征提取和索引建立。
Raw Data
│
▼
Async Processing
│
▼
Filtering / Deduplication / Scenario Mining
│
▼
High-value Data
│
▼
Annotation / Dataset / Analytics
重处理、转码和索引建立都不应阻塞上传请求。不过“完成上传”和“开始异步处理”之间应当有一道清晰边界:前者是控制面中的持久业务状态,后者才是事件流驱动的计算。
Create upload session ──→ PostgreSQL: upload = CREATED
│
▼
Direct upload to storage
│
▼
Verify object ──────────→ PostgreSQL: upload = READY
│
▼
outbox / data.object.ready
│
▼
Kafka
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
transcode quality check build index
设备注册、上传会话、对象元数据和状态转换是低频、需要查询与审计的业务事实,直接放在 PostgreSQL 很合适。对象验证成功后,在同一个数据库事务中写入 outbox;后台 relay 再可靠发布 data.object.ready 一类事件,避免“数据库已经是 READY、但事件没有发出去”或反过来的双写问题。outbox 解决的是本地状态与待发布事件的原子性,不会自动带来端到端 exactly-once:消费者仍应按至少一次投递设计,以 object_id + processor_version 等唯一键实现幂等,并配合重试、DLQ、consumer lag 监控和定期对账。
Kafka 在这里不是替代业务数据库,而是把转码、质量检查、特征提取、索引和后续标注解耦。不同 worker 可以各自伸缩、重试或回放同一份事件,而上传 API 不必等待几十分钟的处理完成。
在机器学习场景,筛选后的数据会进入标注和训练;在其他场景,它可能进入报表、异常检测或审计系统。无论下游是什么,数据集合都不应只是某个 bucket 下的目录。
一个可复用的数据集至少应该具备明确语义、成员清单、版本和血缘:
Raw Object
│
▼
Dataset v17
│
▼
Annotation / Transform v4
│
▼
Training Job / Analysis Job
│
▼
Model / Report / Deployment
有了版本和数据血缘(lineage),才能回答这些关键问题:某个线上结果使用了哪批数据?某次规则变更影响了哪些样本?发现质量问题时,需要回滚数据集、标注还是模型?这既是可复现性的基础,也是排障和审计的基础。
身份、权限与设备状态
长期在线的 Edge 设备需要稳定的机器身份。mTLS 很适合解决“你是谁”:私钥最好在 TPM、Secure Element 或等价硬件中生成并保持不可导出,设备在出厂或注册阶段获得短期、可轮换的证书;服务端在 TLS 握手中验证受信任 CA 的签名与私钥持有证明。设备禁用、证书轮换、吊销或再注册,也应成为 Device Registry 的显式状态。
但证书身份不等于业务状态。设备的型号、租户、固件、状态和权限应放在 Device Registry 里:
mTLS Certificate → source_id
│
▼
Device Registry
├── active?
├── tenant?
├── firmware?
└── permissions?
认证回答“你是谁”,授权回答“你能做什么”。简单系统可以在服务端根据设备身份查询 ACL;复杂系统也可以在边缘到网关使用 mTLS、网关到内部服务使用短期 token。把这两件事分开,证书轮换、设备禁用和权限变更都会更清晰。
Device Registry 与 Device State 是两套数据
同样不要把“设备数量”直接等同于“数据库压力”。10,000 台设备不等于 10,000 QPS,更不等于 10,000 个数据库连接。更重要的是,把低频的设备注册信息和高频的设备运行状态分开。
| 数据 | 例子 | 更合适的归宿 |
|---|---|---|
| Device Registry | 设备型号、租户、证书身份、固件、启停状态、权限 | PostgreSQL |
| Upload Session | 上传 ID、对象 URI、checksum、状态机、审计记录 | PostgreSQL |
| Heartbeat / Telemetry | 在线心跳、温度、位置、CPU、GPU、传感器读数 | Kafka / Redis / TSDB |
| Device State View | “当前是否在线”、最近位置、最新告警 | 消费事件后计算,写 Redis 或业务库 |
例如设备每五秒上报一次 heartbeat,10,000 台设备就是约 2,000 条事件/秒。若每一条都直接 UPDATE device SET last_seen = ...,会制造持续的行更新、WAL 和 vacuum 压力,却未必带来同等价值。让 heartbeat 和 telemetry 先进入 Kafka(或同类事件流),在线检测、聚合、监控和告警分别消费;Redis 可以保存带 TTL 的低延迟当前视图,TSDB 保存时间序列历史。在线状态由 last_seen 加容忍窗口推导,还需处理迟到和乱序事件;PostgreSQL 则按节流、聚合或状态变化落盘权威快照。低量场景直接批量 upsert 到 PostgreSQL 也完全合理。
Event Stream ≠ Durable Business State
这条界线能避免每个 heartbeat 都触发一次昂贵的数据库更新,也让在线检测、监控和状态计算可以独立演进。事件流可以重放,当前状态则可以重新计算;它们不是互相替代,而是不同的读写模型。
一张完整的架构图
把以上部分组合起来,通用的 Edge-to-Cloud 闭环大致是:

看图时只需先抓三条主线:设备注册和上传会话进入 PostgreSQL;大对象由 Edge 直传对象存储;高频状态和 READY 事件进入 Kafka 或专用状态系统。之后由异步 worker 生成可追溯的数据资产,再把结果反馈回现场。图中最重要的不是某一个组件,而是四条边界:控制面不搬运大文件;对象存储保存 payload、PostgreSQL 保存可查询的 metadata 与业务状态;Kafka 承载可重放的事件而不是权威状态;Edge 与 Cloud 分别处理各自最擅长的预处理工作。
具体产品可以替换:机器人、车辆、工厂产线、安防摄像头或智能客户端;具体技术也可以替换:S3、OSS 或 MinIO,Kafka 或其他消息系统,PostgreSQL 或兼容的元数据存储。但边界应当稳定:控制和大流量分开,payload 和 metadata 分开,同一数据集可以追溯到来源和处理过程。
最后:它首先是数据工程
多模态数据平台表面上很像一个 AI 专属问题:采集视频、筛选 bad case、标注、训练、部署。但从后端和分布式系统的角度看,它仍然建立在熟悉的工程能力之上:
Edge Computing
+ Reliable Data Transfer
+ Object Storage
+ Metadata Management
+ Event Streaming
+ Async Processing
+ Distributed State Machine
+ Dataset Versioning
+ Observability
+ Machine Identity
真正变化的是数据的体积、模态和业务语义,而不是这些基本原则。先把采集、传输、存储、状态和数据血缘做扎实,再逐步增加筛选、标注、训练或分析能力,通常比一开始就搭一套“大而全”的平台更稳。
对我来说,从机器人数据平台延伸到这里最大的收获是:所谓闭环,不是把数据无限搬到云上,而是持续把合适的数据送到合适的处理链路,让每一次生产运行都能为下一次迭代留下可用的证据。
参考资料
- Amazon S3:Presigned URL 上传与下载:临时授权直传对象存储,以及上传时的 checksum 校验。
- Amazon S3:Multipart Upload:分片上传、并行传输和单个分片失败后的重传机制。
- Apache Kafka:Event Streaming 简介:以事件流捕获、持久保存、处理和路由来自设备、传感器与应用的数据。
- Debezium:Outbox Event Router:以 outbox 表和变更捕获避免服务内状态与下游事件不一致的一种实现方式。
- Amazon S3:清理未完成 Multipart Upload:通过 lifecycle 规则回收未完成上传的分片。
- SPIFFE:Working with SVIDs:工作负载身份、X.509 SVID、信任根与 mTLS 通道的实践说明。
- SPIFFE:X.509-SVID 规范:使用 URI SAN 承载 SPIFFE ID,以及 X.509 身份的验证要求。