工业场景下时序库与实时计算一体化架构选型实践对比

工业现场的数据链路,很多时候比互联网后端要"拧巴"。一个中型产线,可能有几百个 PLC、传感器、仪表在持续上报数据,采样频率从几百毫秒到几秒不等。这些数据先落到采集网关,再进消息队列,然后一部分要存下来做历史查询,另一部分要立刻算------比如判断某个温度是不是连续超限、某个振动值是不是在恶化。

传统做法是时序库存一份,实时计算框架读一份,两边各管各的。数据在中间搬来搬去,延迟和运维成本都上去了。这两年"时序库+实时计算一体化"的说法越来越多,但到底哪种组合适合自己,很多团队其实没想清楚。

这篇文章不打算给一个"标准答案",因为工业场景差异太大。我想做的是把几种主流方案拉出来,从接入方式、延迟、运维复杂度几个角度对比,并给出可以实际跑起来的代码片段,让你能自己判断。

先明确"一体化"到底指什么

在讨论方案之前,得先把概念说清楚。所谓一体化,通常指下面几种情况之一:

  1. 时序数据库自己带流式计算能力,写入的同时就能触发规则运算;
  2. 时序库和计算引擎深度集成,比如共享存储层或统一 SQL 接口;
  3. 以消息队列为核心,时序库和计算引擎都作为下游消费者,数据只写一次。

这三种思路的取舍点完全不同。第一种省事但计算能力受限,第二种灵活但对版本和生态有要求,第三种解耦最好但链路最长。下面逐个说。

方案一:时序库自带流计算

很多时序数据库近些年都在往"库+计算"方向走。以 TDengine 为例,它提供了流式计算(Stream)能力,可以在建流的时候指定触发条件,数据写入时自动计算并写入结果表。类似的思路在 InfluxDB 的任务系统、TimescaleDB 的连续聚合里也能看到影子。

这种方案最大的好处是链路短。数据写入即触发计算,不需要额外的计算框架,也不需要把数据再读出来。对于"阈值判断""滑动窗口聚合"这类相对固定的计算,非常合适。

我用 Python 写一个简化示例,模拟通过 REST 接口写入数据并建立流计算任务。这里不写具体版本的 API 参数,因为各版本接口有差异,建议以你实际部署版本的官方文档为准。

python 复制代码
import requests
import time
import random

# 假设 TDengine 的 REST 接口地址,实际以你的部署为准
BASE_URL = "http://localhost:6041/rest/sql"
AUTH = ("root", "taosdata")

def exec_sql(sql: str):
    resp = requests.post(
        BASE_URL,
        data=sql.encode("utf-8"),
        auth=AUTH,
        timeout=5,
    )
    resp.raise_for_status()
    return resp.json()

# 建库建表(简化,未加保留策略等参数)
exec_sql("CREATE DATABASE IF NOT EXISTS factory")
exec_sql("USE factory")
exec_sql(
    "CREATE TABLE IF NOT EXISTS sensor_temp "
    "(ts TIMESTAMP, device_id NCHAR(32), temp FLOAT)"
)

# 写入模拟数据
now = int(time.time() * 1000)
for i in range(20):
    ts = now + i * 1000
    temp = 60 + random.uniform(-5, 15)
    exec_sql(
        f"INSERT INTO sensor_temp VALUES "
        f"({ts}, 'dev_001', {temp:.2f})"
    )

# 建一个流:温度超过 70 时写入告警表
# 具体语法请以你使用的版本为准,这里只表达思路
exec_sql(
    "CREATE STREAM IF NOT EXISTS temp_alarm "
    "INTO temp_alarm_table AS "
    "SELECT ts, device_id, temp FROM sensor_temp WHERE temp > 70"
)

这段代码的重点不在语法本身,而在于计算逻辑被下推到了数据库内部。你不需要维护一个 Flink 集群,也不需要写消费逻辑。对于规则相对固定的场景,这是最省心的路径。

但它也有明显短板。流计算能力通常只覆盖 SQL 能表达的运算,一旦你需要调用外部模型、做复杂状态管理、或者跨多个数据源关联,就会很吃力。所以我的判断是:规则简单、变化少、团队没有专职流计算开发,优先考虑这条路。

如果计算逻辑复杂,或者需要和别的数据源做关联,Flink 这类流计算框架仍然是主流选择。它的优势是状态管理成熟、Exactly-Once 语义有保障、生态丰富。

问题在于,Flink 和时序库之间怎么衔接。常见做法有两种:一是 Flink 直接读时序库的变更,二是 Flink 从消息队列消费,算完再写回时序库。

第一种做法依赖时序库的 CDC 能力,不是所有库都支持得好。第二种更通用,但意味着数据要先进消息队列。

我用 PyFlink 写一个最小示例,展示从 Kafka 消费、做窗口聚合、再写回外部存储的骨架。注意 PyFlink 的版本差异较大,下面代码基于较新的 1.17+ 风格,老版本 API 不同。

python 复制代码
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.connectors.kafka import (
    KafkaSource,
    KafkaOffsetsInitializer,
)
from pyflink.common.serialization import SimpleStringSchema
from pyflink.common import WatermarkStrategy, Duration, Types
from pyflink.datastream.functions import MapFunction


class ParseSensor(MapFunction):
    """把 Kafka 里的 JSON 字符串解析成元组"""

    def map(self, value):
        import json
        obj = json.loads(value)
        return (obj["device_id"], obj["ts"], float(obj["temp"]))


def build_job():
    env = StreamExecutionEnvironment.get_execution_environment()
    env.set_parallelism(2)

    source = (
        KafkaSource.builder()
        .set_bootstrap_servers("localhost:9092")
        .set_topics("sensor_raw")
        .set_group_id("flink_sensor_group")
        .set_starting_offsets(KafkaOffsetsInitializer.latest())
        .set_value_only_deserializer(SimpleStringSchema())
        .build()
    )

    stream = env.from_source(
        source,
        WatermarkStrategy.for_bounded_out_of_orderness(
            Duration.of_seconds(5)
        ),
        "kafka_sensor_source",
    )

    parsed = stream.map(
        ParseSensor(),
        output_type=Types.TUPLE(
            [Types.STRING(), Types.LONG(), Types.FLOAT()]
        ),
    )

    # 这里做窗口聚合,比如 10 秒内每个设备的平均温度
    # 实际写回时序库需要自定义 Sink,此处省略具体实现
    agg = parsed.key_by(lambda x: x[0]).count_window(10)

    agg.print()

    env.execute("sensor_aggregation_job")


if __name__ == "__main__":
    build_job()

这段代码只是骨架,真正落地时,Sink 部分需要你自己实现,或者用现成的连接器。这也是这个方案的一个现实问题:集成工作量大,且很多连接器质量参差不齐。

【踩坑提醒】PyFlink 和 Flink 的版本必须严格对齐,尤其是连接器依赖。用 pip 装 pyflink 时,Kafka 连接器往往需要单独下载 jar 并放到指定目录,否则运行时会报类找不到。这一点我建议在测试环境先跑通最小链路,再上生产。

方案三:消息队列为中心

第三种思路是把消息队列(Kafka、Pulsar、EMQX 等)放在中心位置。采集端只往队列写,时序库和计算引擎都作为消费者,各自处理自己关心的部分。

这种架构的解耦性最好。时序库挂了不影响计算,计算逻辑改了不影响存储。工业场景里,设备协议五花八门,采集层经常要独立演进,这种解耦的价值其实很高。

代价是链路变长,端到端延迟会增加。而且消息队列本身也需要运维,多了一套要监控的东西。

下面用一个简单的 Python 消费者示例,模拟从 MQTT 订阅并分流到不同下游的场景。工业现场 MQTT 用得很多,这里用 paho-mqtt。

python 复制代码
import json
import paho.mqtt.client as mqtt

# 简单分流:正常数据进时序库,异常数据额外告警
def on_message(client, userdata, msg):
    try:
        payload = json.loads(msg.payload.decode("utf-8"))
    except json.JSONDecodeError:
        return

    device_id = payload.get("device_id")
    temp = payload.get("temp")
    ts = payload.get("ts")

    if temp is None:
        return

    # 写入时序库(这里只打印,实际替换成写入调用)
    print(f"store: {device_id} {ts} {temp}")

    # 超限走另一条路径
    if temp > 70:
        print(f"alarm: {device_id} temp={temp}")


client = mqtt.Client()
client.on_message = on_message
client.connect("localhost", 1883, 60)
client.subscribe("factory/sensor/#")
client.loop_forever()

这种写法的好处是逻辑直观,扩展容易。但要注意,Python 消费者在高吞吐下会成为瓶颈,实际生产里通常用多进程或者换成 Java/Go 实现。Python 更适合做原型验证和中小规模场景。

三种方案的对比

把上面的内容整理成一张表,方便对照。

方案 优点 缺点 适用场景
时序库自带流计算 链路短,无额外组件,运维简单 计算能力受 SQL 限制,难做复杂状态 规则固定、规模中等、团队小
时序库 + Flink 计算能力强,状态管理成熟,生态好 集成复杂,版本依赖敏感,运维成本高 计算逻辑复杂、需要多源关联
消息队列为中心 解耦彻底,各组件独立演进 链路长,延迟增加,多一套运维 采集层复杂、需要多下游消费

这张表只能作为起点。实际选型还要看你的数据量级、延迟容忍度、团队技术栈。

怎么选:几个判断维度

我不想给一个"选 X 就对了"的结论,因为工业场景差异太大。但有几个维度可以先想清楚:

数据规模和频率。如果每秒写入只有几千条,时序库自带流计算基本够用。如果到了几十万条每秒,Flink 的并行处理能力会更稳。

延迟要求。要求亚秒级响应,链路越短越好,方案一或方案三配合轻量消费者更合适。如果允许秒级甚至分钟级延迟,Flink 的窗口聚合完全没问题。

计算复杂度。纯阈值判断和滑动窗口,SQL 能表达,方案一足够。涉及状态机、跨流关联、外部模型调用,方案二更合适。

团队能力。如果团队主要写 Python 和 SQL,没有专职流计算开发,硬上 Flink 会拖慢进度。方案一和方案三的 Python 实现更容易维护。

运维预算。每多一个组件,就多一套监控、告警、升级流程。方案三虽然解耦好,但消息队列本身也是要人管的。

一点个人判断

从我这几年接触的工业项目看,很多团队其实高估了自己的计算需求。真正需要 Flink 级别能力的场景,比例并不高。大量所谓"实时计算",本质就是阈值判断和简单聚合,用时序库自带的流计算完全能覆盖。

反过来,也有团队低估了解耦的价值。采集层一旦和计算逻辑耦合太紧,后面设备协议变了、采集频率调了,改动就会牵一发动全身。

我的倾向是:先用最简单的方案跑通,把链路和数据质量验证清楚,再根据实际瓶颈决定要不要引入更重的组件。一上来就搭 Flink 集群,很多时候是在为想象中的需求买单。

【注意】本文涉及的 TDengine、Flink、Kafka、MQTT 相关代码均为思路演示,具体 API 参数、连接器配置、版本兼容性请以你实际使用的版本官方文档为准。我没有在文中声称任何具体版本号或性能数据,因为这些和部署环境强相关。

如果你正在做类似选型,建议先拿一条真实产线的数据做小规模验证,重点看端到端延迟和异常恢复表现,而不是只看压测数字。

=备用标题=

  1. 工业时序数据实时计算:三种一体化架构的落地对比
  2. 时序库自带流计算够用吗?工业实时计算方案选型分析
  3. 从采集到告警:工业数据库实时计算一体化方案怎么选
  4. 工业场景下时序库与流计算框架的组合方式与取舍
  5. Python 视角下的工业时序数据实时计算架构对比
相关推荐
余槐i1 小时前
MySQL 深分页从 4 秒到 60 毫秒:延迟关联减少回表的实测与代价
mysql·性能优化·sql优化·执行计划·慢查询优化
ZealSinger1 小时前
Java虚拟线程上线后瓶颈转移到哪看三层
java·开发语言·firefox
2601_961133281 小时前
盘立方软件指标文华期货公式下载
java
东方佑1 小时前
事件发生与智能:微观一致性与宏观涌现性
人工智能·深度学习·自然语言处理·架构·gru
无名猿1 小时前
shared_ptr 完全指南:引用计数、控制块与开销
c++·性能优化·内存管理·标准库·现代c++
蜗牛互联网1 小时前
长任务多Agent共享文件系统的Manifest交接模式
java·人工智能·后端
梦幻精灵_cq1 小时前
sumdir——一个伪python.built-in的“术法”打造
python
欣欣之王来了1 小时前
Python入门:什么是Python以及为什么选择它
学习·架构·面向对象·项目·python教程
Sirens.1 小时前
Java并发锁详解:六类锁策略与 synchronized 底层原理
java·前端·算法