使用 ES|QL 调试 LLM 延迟、成本与 GPU 饱和度

Dashboard 告诉你"出问题了";ES|QL 告诉你"为什么"。

通过三个 ES|QL 查询,基于 OpenTelemetry 追踪数据排查 LLM 的延迟异常、Token 成本飙升和 GPU 饱和问题------不仅找到症状,更要定位根因。

通过针对 OpenTelemetry 追踪数据的三个查询,我们发现了:一次模型替换导致的 2.4 倍成本回归 、某个提示模板比另一个多产生 23 倍输出 Token、以及在 43 个时间窗口中有 42 个窗口 GPU 利用率超过 90%。而这一切,都来自于同一个已经通过 EDOT 发送追踪数据、通过 DCGM 发送 GPU 指标的集群。


前置条件

  • Elasticsearch 9.x+
  • Python 3.9+
  • Ollama v0.5.12+(本地安装)

AI 工作负载中的可观测性缺口

大多数运行 LLM 应用的团队已经完成了第一步:通过 EDOT、OpenLIT、Langtrace 等工具为应用添加追踪(Trace)埋点,捕获延迟、Token 用量等数据。数据已经在流动。

但下一步------当问题发生时如何查询这些数据------才是真正的挑战。

预置的 Dashboard 只能回答预设问题:"P95 延迟是多少?""今天用了多少 Token?"这些对监控有用,但对调试 (Debugging)远远不够。调试意味着你有一个症状("上周二延迟突然飙升"),需要在数据中探索 直到找到根因。这种探索需要的是查询语言,而不是固定的仪表盘。

这就是 ES|QL 的价值所在。

ES|QL

ES|QL 是 Elasticsearch 的管道式查询语言,可以在单一查询中完成聚合、过滤、跨索引关联。应用于 LLM 遥测数据时,它能让你:

  • 在一个查询中对比不同模型版本的 P95 延迟
  • 按自定义提示标识符分组,找到那个"烧钱"的模板
  • 将 LLM 追踪数据与 GPU 指标关联,判断基础设施是否为瓶颈

在之前的文章中,我们介绍了如何采集 LLM 遥测数据(通过 EDOT、OpenLIT 或 Langtrace)。本文则专注于:当出现问题时,如何调查这些数据。


技术栈:LLM 遥测数据如何进入 Elastic

在调试之前,我们需要了解有哪些数据可用、它们存储在哪里。
Elastic 集群
基础设施层
应用层
调用
EDOT 自动埋点
DCGM Exporter
OTel Collector
Elastic Managed OTLP Endpoint
Elastic Managed OTLP Endpoint
Python 应用

OpenAI Client
Ollama

localhost:11434
OTel Traces
NVIDIA GPU
Prometheus Metrics

:9400
OTel Metrics
traces-generic.otel-default
metrics-nvidia_gpu.otel-default

整个技术栈包含两条数据路径:

路径一:LLM 追踪(应用层)

你的 Python 应用通过 OpenAI 客户端调用 Ollama(或任何兼容 OpenAI API 的端点)。EDOT Python 会自动为这些调用添加埋点,生成遵循 OpenTelemetry GenAI 语义约定 的 Span。

当这些数据通过 Elastic Managed OTLP Endpoint 发送后,会落入 Elasticsearch 的 traces-generic.otel-default 数据流中。

路径二:GPU 指标(基础设施层)

在运行 GPU 推理的主机上,NVIDIA 的 DCGM Exporter 将 GPU 指标以 Prometheus 格式暴露在 9400 端口。OpenTelemetry Collector 采集这些指标并转发到 Elasticsearch,落入 metrics-* 数据流。


EDOT 自动捕获了哪些数据?

EDOT Python 包含 elastic-opentelemetry-instrumentation-openai,它会自动为 OpenAI 客户端库的每次调用添加埋点。由于 Ollama 在 http://localhost:11434/v1/ 提供了兼容 OpenAI 的 API,EDOT 可以在无需修改代码的情况下对 Ollama 调用进行埋点。

每次 LLM 调用会产生一个 Span,包含以下属性(遵循 OTel GenAI 语义约定):

属性 含义 示例
gen_ai.operation.name 操作类型 chat
gen_ai.request.model 请求的模型 gemma4:e4b
gen_ai.response.model 实际响应的模型 gemma4:e4b
gen_ai.usage.input_tokens 输入 Token 数 142
gen_ai.usage.output_tokens 输出 Token 数 89
gen_ai.response.id 唯一完成 ID chatcmpl-abc123

在 Kibana 的 APM 追踪视图中,每次调用都会显示为一个带有上述属性的 Span:
Trace: chat
Span: chat

gen_ai.operation.name: chat
Attributes
gen_ai.request.model: gemma4:e4b
gen_ai.usage.input_tokens: 142
gen_ai.usage.output_tokens: 89
gen_ai.response.id: chatcmpl-abc123

此外,EDOT 还会发出两个指标:

  • gen_ai.client.token.usage:Token 数量的直方图
  • gen_ai.client.operation.duration:请求延迟的直方图(秒)

配置极其简单,只需将 OpenAI 客户端指向 Ollama,并用 EDOT 的自动埋点运行:

python 复制代码
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1/",
    api_key="ollama",  # 客户端要求提供,但 Ollama 并不使用
)

如何为 OTel Span 添加自定义提示模板 ID

OTel GenAI 语义约定涵盖了模型追踪和 Token 用量,但没有包含提示模板标识符。如果你运行着多个提示模板(系统提示、少样本变体等),你需要知道是哪个模板导致了问题。

当前 OTel 规范中不存在 gen_ai.prompt.id 这样的约定。为了填补这个空白,你可以添加自定义 Span 属性

python 复制代码
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("prompt-execution") as span:
    span.set_attribute("prompt.template.id", "summarize-v2")
    response = client.chat.completions.create(
        model="gemma4:e4b",
        messages=[{"role": "user", "content": prompt}]
    )

这个 prompt.template.id 属性会随 Span 一起流入 Elasticsearch,你可以像使用任何内置属性一样在 ES|QL 查询中使用它。


GPU 指标:从 DCGM 到 Elastic

对于在 NVIDIA 硬件上运行自托管模型的团队,GPU 指标是关键的上下文信息。NVIDIA 的 DCGM(Data Center GPU Manager)Exporter 将 GPU 利用率、显存使用、温度、功耗等指标以 Prometheus 格式暴露在 9400 端口。

OpenTelemetry Collector 通过 Prometheus Receiver 采集这些指标并转发到 Elastic。Resource Processor 为每个指标打上 data_stream.dataset = nvidia_gpu 的标签,将数据路由到 metrics-nvidia_gpu.otel-default 数据流,与 Elastic 的 NVIDIA GPU OpenTelemetry 集成对齐:

yaml 复制代码
receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: nvidia_gpu
          scrape_interval: 10s
          static_configs:
            - targets: ["localhost:9400"]

processors:
  resource/nvidia_gpu:
    attributes:
      - key: data_stream.dataset
        value: nvidia_gpu
        action: upsert
      - key: data_stream.namespace
        value: default
        action: upsert

exporters:
  otlp:
    endpoint: "${OTEL_EXPORTER_OTLP_ENDPOINT}"

service:
  pipelines:
    metrics:
      receivers: [prometheus]
      processors: [resource/nvidia_gpu]
      exporters: [otlp]

Elastic 提供了原生的 NVIDIA GPU OpenTelemetry 集成,包含 Fleet 级别的 Dashboard、六个告警规则(如热节流检测)以及 GPU 热健康 SLO 模板。

LLM 调试的关键 GPU 指标:

指标 含义
DCGM_FI_DEV_GPU_UTIL GPU 计算单元有多忙(%)
DCGM_FI_DEV_FB_USED GPU 显存(VRAM)消耗了多少
DCGM_FI_DEV_GPU_TEMP 是否可能因热节流影响性能
DCGM_FI_DEV_POWER_USAGE 功耗,可指示持续高负载

注意 :DCGM 需要 NVIDIA 数据中心级 GPU(A100、H100、L40S)。对于消费级 GPU,可使用基于 NVML 的工具(如 nvmlreceiver)。云托管 LLM 提供商(OpenAI、Bedrock、Azure OpenAI)完全不暴露 GPU 指标,因为硬件已被抽象。


问题一:新版本模型是否拖慢了速度、抬高了成本?

场景

你一直在生产环境使用 gemma4:e2b,刚刚部署了 gemma4:e4b 以获得更好的生成质量。几天后,延迟告警触发,Token 账单也上涨了。是模型替换导致的吗?

关键字段:request.model vs response.model

gen_ai.request.model(你请求的模型)和 gen_ai.response.model(实际响应的模型)之间的区别很重要。使用 Ollama 时,两者通常与你指定的 model:tag 一致。但在使用云提供商的模型别名时(如 gpt-4o 解析到特定固定版本),响应模型可能与请求不同。

对于模型版本对比,gen_ai.response.model 是更可靠的字段,因为它反映了实际运行的模型。

ES|QL 查询

esql 复制代码
FROM traces-generic.otel-default
| WHERE attributes.gen_ai.operation.name == "chat"
  AND @timestamp >= NOW() - 7 days
| EVAL is_failure = CASE(attributes.event.outcome == "failure", 1, 0)
| STATS
    request_count = COUNT(*),
    avg_input_tokens = AVG(attributes.gen_ai.usage.input_tokens),
    avg_output_tokens = AVG(attributes.gen_ai.usage.output_tokens),
    p95_duration_us = PERCENTILE(transaction.duration.us, 95),
    error_count = SUM(is_failure)
  BY attributes.gen_ai.response.model
| SORT p95_duration_us DESC

这个查询提供了并排对比:每个模型版本在延迟、Token 用量和错误率上的表现。针对从两个 Gemma 4 变体收集的 120 条 chat Span 运行此查询,结果如下:

模型 请求数 平均输入 Token 平均输出 Token P95 延迟 错误数
gemma4:e4b 60 99 85 12.3s 0
gemma4:e2b 60 99 92 5.1s 0

两个关键发现:

  1. 输入提示完全相同 (两边都是 99 个输入 Token),因此延迟差距不是由提示长度驱动的。
  2. gemma4:e4b 的平均输出 Token 实际上更少 ,但 P95 延迟却超过两倍 。这说明回归出在模型本身,而非工作负载。

用 LOOKUP JOIN 添加成本维度

OTel GenAI 规范不包含成本属性。Token 数量可用,但要换算成成本,需要知道每个模型的定价。这正是 ES|QL 的 LOOKUP JOIN 的用武之地。

第一步:创建定价查找索引

json 复制代码
PUT /model_pricing
{
  "settings": {
    "index": {
      "mode": "lookup"
    }
  },
  "mappings": {
    "properties": {
      "attributes.gen_ai.response.model": { "type": "keyword" },
      "cost_per_1k_input_tokens": { "type": "float" },
      "cost_per_1k_output_tokens": { "type": "float" }
    }
  }
}

填入你的模型定价数据,然后扩展查询:

esql 复制代码
FROM traces-generic.otel-default
| WHERE attributes.gen_ai.operation.name == "chat"
  AND @timestamp >= NOW() - 7 days
| STATS
    request_count = COUNT(*),
    total_input_tokens = SUM(attributes.gen_ai.usage.input_tokens),
    total_output_tokens = SUM(attributes.gen_ai.usage.output_tokens),
    p95_duration_us = PERCENTILE(span.duration.us, 95)
  BY attributes.gen_ai.response.model
| LOOKUP JOIN model_pricing ON attributes.gen_ai.response.model
| EVAL estimated_cost =
    (total_input_tokens / 1000.0) * cost_per_1k_input_tokens +
    (total_output_tokens / 1000.0) * cost_per_1k_output_tokens
| SORT estimated_cost DESC

现在,你可以在单一结果集中看到每个模型版本的延迟和成本。LOOKUP JOIN 在查询时丰富了追踪数据,无需将定价信息冗余到每个 Span 中。

假设 gemma4:e2b 的定价为 0.10/0.30(输入/输出,每 1K Token),gemma4:e4b0.25/0.75,同样的 60 次请求产生:

模型 总输入 Token 总输出 Token 估计成本
gemma4:e4b 5,940 5,100 $5.34
gemma4:e2b 5,940 5,520 $2.21

gemma4:e4b 的工作负载成本约为 gemma4:e2b 的 2.4 倍 ,尽管它生成的输出 Token 还略少一些。延迟回归和成本回归,一个查询全部定位。
STATS BY model
LOOKUP JOIN
追踪数据

traces-generic.otel-default
聚合结果
定价表

model_pricing
输出: 延迟 + 成本

并排对比

何时使用模型版本对比查询?

此模式适用于任何评估模型变更的场景:模型版本的 A/B 测试、渐进式发布、或基于请求复杂度的多模型路由策略。


问题二:哪个提示模板在疯狂消耗 Token?

场景

本周 Token 用量飙升了 40%,但你没有更换模型。你有三个提示模板在轮换使用(摘要、提取、分类),需要找出罪魁祸首

为什么 prompt.template.id 值得添加?

OTel GenAI 语义约定追踪了处理请求的模型、使用的 Token 数量、耗时,但不追踪使用了哪个提示模板------因为提示管理是应用特定的。

这个缺口对调试影响很大。如果所有提示都通过同一个 gen_ai.operation.name == "chat" 操作,没有自定义标识符,你就无法区分表现良好的摘要提示和失控的提取提示。

添加 prompt.template.id 作为自定义 Span 属性(如前文所示)解决了这个问题。这是一个值得尽早采用的模式,因为"没有它的代价"只有在出问题时才显现。

ES|QL 查询

esql 复制代码
FROM traces-generic.otel-default
| WHERE attributes.gen_ai.operation.name == "chat"
  AND @timestamp >= NOW() - 7 days
| EVAL is_failure = CASE(attributes.event.outcome == "failure", 1.0, 0.0)
| STATS
    request_count = COUNT(*),
    avg_output_tokens = AVG(attributes.gen_ai.usage.output_tokens),
    max_output_tokens = MAX(attributes.gen_ai.usage.output_tokens),
    error_rate = AVG(is_failure) * 100
  BY attributes.prompt.template.id
| SORT avg_output_tokens DESC

针对 120 条 Span 运行此查询,结果一目了然:

提示模板 请求数 平均输出 Token 最大输出 Token 错误率
extraction-v3 40 458 2,847 0%
summarize-v2 40 89 156 0%
classify-v1 40 20 23 0%

extraction-v3 每次请求产生的 Token 约为 summarize-v2 的 5 倍,约为 classify-v1 的 23 倍。

max_output_tokens 列也很重要:少数极端响应可能拉高平均值,同时看平均值和最大值可以确认 extraction-v3结构性的话痨,而非被单个异常值扭曲。
所有 chat 操作
prompt.template.id 分组
extraction-v3

avg: 458 tokens
summarize-v2

avg: 89 tokens
classify-v1

avg: 20 tokens
🔥 Token 消耗大户

扩展到其他调试维度

prompt.template.id 模式可以扩展到任何你想切分的调试维度:客户等级、用例、部署区域。都可以作为自定义 Span 属性添加,并在 ES|QL 中分组。GenAI 约定给了你模型和 Token 层,自定义属性给了你业务上下文层


问题三:LLM 延迟是否与 GPU 饱和度相关?

场景

过去一周推理延迟逐渐增加,但应用代码和模型都没有变化。你怀疑是基础设施的问题。

这个问题是自托管模型独有的 。使用云 LLM 提供商(OpenAI、Bedrock、Azure OpenAI)时,GPU 资源完全抽象,你只能看到延迟飙升,但无法检查提供商的 GPU 是否饱和。而在自托管的 NVIDIA 硬件上,你可以同时看到等式的两边

GPU 指标告诉我们什么?

来自 DCGM Exporter 的 GPU 指标为推理引擎提供了窗口:

  • DCGM_FI_DEV_GPU_UTIL(>90%):GPU 计算单元饱和,新的推理请求排队,延迟增加。
  • DCGM_FI_DEV_FB_USED 接近总显存:GPU 显存压力,模型层可能需要交换,或 GPU 无法批量处理那么多请求。
  • DCGM_FI_DEV_GPU_TEMP 升高:一旦超过 GPU 的节流阈值,会触发热节流,GPU 降低时钟频率以管理热量,直接影响推理吞吐量。

关联追踪与 GPU 指标

挑战在于 LLM 追踪和 GPU 指标存储在不同索引 中,拥有不同 Schema

  • LLM Span 在 traces-generic.otel-default 中,时间戳是请求级别
  • GPU 指标在 metrics-* 中,时间戳是采集间隔(通常每 10-15 秒)

ES|QL 的 LOOKUP JOIN 可以将它们关联起来。思路是:创建查找索引 → 将 GPU 指标聚合成每分钟桶 → 索引这些桶 → 将追踪数据与之关联
关联查询
预处理
原始数据
DATE_TRUNC 1 minute

AVG 聚合
DATE_TRUNC 1 minute
LOOKUP JOIN
LLM Spans

traces-generic.otel-default

时间戳: 请求级
GPU Metrics

metrics-*<

时间戳: 10s 采集间隔
gpu_metrics_by_minute

每分钟桶
按分钟聚合 Span
结果: 延迟 + GPU 利用率

同时间窗口对齐

第一步:创建存放聚合 GPU 指标的查找索引

json 复制代码
PUT /gpu_metrics_by_minute
{
  "settings": {
    "index": {
      "mode": "lookup"
    }
  },
  "mappings": {
    "properties": {
      "time_bucket": { "type": "date" },
      "gpu_utilization": { "type": "float" },
      "gpu_memory_used": { "type": "float" },
      "gpu_temperature": { "type": "float" }
    }
  }
}

第二步:将原始 DCGM 指标聚合成每分钟桶

esql 复制代码
FROM metrics-*
| WHERE metrics.DCGM_FI_DEV_GPU_UTIL IS NOT NULL
  AND @timestamp >= NOW() - 7 days
| EVAL time_bucket = DATE_TRUNC(1 minute, @timestamp)
| STATS
    gpu_utilization = AVG(metrics.DCGM_FI_DEV_GPU_UTIL),
    gpu_memory_used = AVG(metrics.DCGM_FI_DEV_FB_USED),
    gpu_temperature = AVG(metrics.DCGM_FI_DEV_GPU_TEMP)
  BY time_bucket

使用 Elasticsearch Bulk API 将聚合结果索引到 gpu_metrics_by_minute。在生产环境中,可以使用 Elasticsearch Transform 自动保持查找索引更新。

第三步:关联追踪数据

esql 复制代码
FROM traces-generic.otel-default
| WHERE attributes.gen_ai.operation.name == "chat"
  AND @timestamp >= NOW() - 7 days
| EVAL time_bucket = DATE_TRUNC(1 minute, @timestamp)
| STATS
    avg_duration_us = AVG(transaction.duration.us),
    request_count = COUNT(*)
  BY time_bucket
| LOOKUP JOIN gpu_metrics_by_minute ON time_bucket
| WHERE gpu_utilization IS NOT NULL
| EVAL latency_vs_gpu = CASE(
    gpu_utilization > 90 AND avg_duration_us > 5000000, "saturated + slow",
    gpu_utilization > 90 AND avg_duration_us <= 5000000, "saturated but ok",
    gpu_utilization <= 90 AND avg_duration_us > 5000000, "slow without gpu cause",
    "normal"
  )
| SORT time_bucket DESC

注意 :由于 GPU 指标每 10 秒采集一次,而 LLM Span 是请求级时间戳,两边需要共同的粒度才能关联。查找索引将原始指标聚合为每分钟平均值 ,追踪侧通过 DATE_TRUNC(1 minute, @timestamp) 将 Span 对齐到同样的桶。

如何解读 latency_vs_gpu 分类

latency_vs_gpu 列将每个时间窗口分类:

分类 含义 行动建议
saturated + slow GPU 是瓶颈 扩展 GPU 容量、减小批次大小、使用更小模型
saturated but ok GPU 繁忙但延迟可接受 接近极限但尚未过载
slow without gpu cause 其他因素导致延迟 排查网络、预处理、队列深度等
normal 一切正常 无需行动

将 120 条 chat Span 与每分钟的 GPU 桶关联后,返回了 43 个同时有 LLM 活动和 GPU 数据的时间窗口:

latency_vs_gpu 分钟数 GPU 利用率范围 平均请求延迟
saturated + slow 42 90.8% - 98.2% 5.98s - 70.5s
saturated but ok 1 93.9% 4.77s

在整个测试窗口中,GPU 利用率持续高于 90%,同时平均请求延迟在每个分钟都保持在 5 秒以上------除了一个分钟 。这正是 saturated + slow 模式:GPU 是瓶颈,不是提示、不是模型加载器、不是网络。

唯一一个 saturated but ok 的分钟(平均 4.77s)展示了阈值所在:GPU 仍然满载,但该分钟的请求混合较轻,使延迟保持在 5 秒以下。


从调试走向告警

一旦你通过临时 ES|QL 查询识别了模式,就可以将其转化为检测规则。Elastic 的告警支持基于 ES|QL 的规则,因此帮助你发现问题的同一个查询,可以成为下次捕获问题的告警:

  • 单个提示模板的 Token 用量超过阈值
  • 模型版本延迟回归超过百分比
  • GPU 利用率持续高于 90% 且推理延迟恶化

Kibana 内置的 LLM 可观测性

对于希望在使用 ES|QL 查询的同时拥有预置视图的用户,Elastic 在 Observability 9.0 中提供了开箱即用的 LLM 可观测性 Dashboard。这些 Dashboard 涵盖 OpenAI、Amazon Bedrock、Azure AI 和 Google Vertex AI,展示 Token 用量、延迟分布和成本分解。

对于 GPU 基础设施,NVIDIA GPU OpenTelemetry 集成 增加了 Fleet 级别的 Dashboard,包含 GPU 利用率、显存、温度和功耗指标,以及六个针对关键 GPU 条件的预配置告警规则。

Dashboard 与 ES|QL 的关系:

  • Dashboard:用于持续监控和健康检查
  • ES|QL:用于深入排查特定问题

两者互补,而非替代。


结论

主题 核心要点
可观测性缺口 采集 LLM 遥测已解决,调试它才是挑战。ES
三大调试模式 1) 模型版本对比(STATS + LOOKUP JOIN)< 2) 提示模板隔离(自定义属性 + GROUP BY)< 3) GPU 关联(跨追踪和指标索引的 LOOKUP JOIN)
LOOKUP JOIN 的价值 在查询时用外部上下文(定价、GPU 指标)丰富追踪数据,无需修改埋点。
自定义属性 prompt.template.id 等域特定字段扩展 OTel GenAI 约定,覆盖规范尚未包含的调试维度。

适用范围

此方法适用于任何通过 EDOT 埋点的兼容 OpenAI 的 LLM 端点(Ollama、vLLM、TGI),且 ES|QL 查询可在任何拥有相应数据流的 Elastic 集群中运行。
调试分析层
Elastic 存储层
数据采集层
STATS / LOOKUP JOIN
GROUP BY 自定义属性
LOOKUP JOIN
EDOT 自动埋点

OpenAI Client
DCGM Exporter

NVIDIA GPU
traces-generic.otel-default
metrics-nvidia_gpu.otel-default
模型对比

成本计算
提示模板分析

Token 效率
GPU 指标关联

根因定位


核心 takeaway:Dashboard 告诉你"有异常",ES|QL 告诉你"为什么有异常"。将两者结合,你就拥有了从监控到调试的完整 LLM 可观测性闭环。

相关推荐
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月21日
大数据·人工智能·python·信息可视化·自然语言处理
Elastic 中国社区官方博客1 小时前
用于调试 LLM 延迟、成本和 GPU 饱和度的 ES|QL 查询
大数据·人工智能·elasticsearch·搜索引擎·ai·云原生·serverless
heimeiyingwang1 小时前
【架构实战】Jenkins+GitLab CI/CD:持续集成与持续部署实践
架构·gitlab·jenkins
weixin_423533991 小时前
windows11安装claude code模型用deepseek,跳过国内校验。
大数据·elasticsearch·搜索引擎
GIS数据转换器1 小时前
基于低空巡检的空地一体智慧治理平台
大数据·人工智能·数据挖掘·数据分析·无人机
商业模式源码开发10 小时前
实体门店低获客成本增长案例:3 人转介绍模型 + 消费返还机制落地分析
大数据·商业模式·私域流量
元拓数智12 小时前
智能分析落地卡壳?先补好「数据关系+语义治理」这层技术基建
大数据·分布式·ai·spark·数据关系·语义治理
TDengine (老段)13 小时前
TDengine Tag 设计哲学与 Schema 变更机制
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
sxgzzn14 小时前
新能源场站数智化转型:基于数字孪生与AI的智慧运维管理平台解析
大数据·运维·人工智能