返回博客
技术13 分钟阅读

实时数据可视化:百万事件/秒的实践与教训

当图表更新速度超过用户处理能力时会发生什么?来自大规模实时系统建设的经验。

Priya Sharma, 高级数据工程师

Priya Sharma

高级数据工程师

Share:
实时数据可视化仪表盘:流式指标、实时更新、性能指标与数据流,采用 ChartGen 专业蓝色调,用于高频监控
构建可扩展至百万事件的实时仪表盘

去年我负责为一个交易平台做实时监控仪表盘。需求是:以百万事件/秒的速率可视化市场数据,从事件发生到用户屏幕的延迟要低于 100ms。

这把我们过去对数据可视化的认知全打破了。

「实时」的真相

没人会告诉你的是:「实时」往往并不是真正的实时。而且很多时候这没问题。

大多数标着「实时」的仪表盘其实是每 5–30 秒刷新一次。对多数场景这已经够用。但当你真的需要亚秒级更新时,规则就完全变了。

「实时」的三个层级

层级 1:近实时(5–60 秒刷新)

典型场景:业务仪表盘、营销分析、销售指标

架构:轮询 API、批量聚合

复杂度:中等

这是大多数人需要的:数据团队每分钟聚合一次,仪表盘轮询更新,简单有效。

层级 2:实时(1–5 秒刷新)

典型场景:运维监控、实时活动追踪、客服队列

架构:WebSocket、服务端推送、流式查询

复杂度:高

从轮询到推送,一切都不一样了:要维护长连接、处理重连、管理客户端/服务端状态。

层级 3:亚秒级(小于 1 秒)

典型场景:交易平台、实时游戏统计、工业监控

架构:流式管道、专用数据库、优化渲染

复杂度:很高

我们在这个层级待了 8 个月,每一处优化、每一毫秒都算数。

实时可视化难在哪

问题 1:数据量

每秒 100 万事件时,不可能逐条渲染——那是每秒 100 万个点,浏览器会崩。

做法:预聚合。不要把原始事件推到前端,在源头按时间桶做均值、计数、分位数等聚合。我们每秒只发 10 次聚合结果,而不是 100 万条原始事件。

问题 2:渲染性能

即使每秒只有 10 次更新,整图重绘也会拖垮性能。React 的调和、SVG 操作、Canvas 重画都会叠加。

做法:增量更新。不要重建整张图,只追加。我们对最高频的图用了基于 WebGL 的渲染,可以稳定跑 60fps 更新。

问题 3:人眼与认知

一个反直觉的结论:更新快于约 200ms 就会糊成一片。用户处理不了 10+ fps 的信息,只会觉得在闪。

做法:视觉平滑。即使数据是 10Hz 更新,我们把过渡动画拉长到 200ms,图表既「活」又不乱。

问题 4:网络不稳定

WebSocket 会断、包会延迟、移动端会切网。

做法:可靠重连、消息队列、优雅降级。断线时展示上次已知状态并显示「重连中」,而不是白屏。

我们用的架构

满足「百万事件/秒」的大致技术栈是:

数据层

  • Apache Kafka 做事件接入
  • Apache Flink 做实时聚合
  • Redis 做最新状态缓存
  • TimescaleDB 做历史查询

API 层

  • Go 写 WebSocket 服务(高并发友好)
  • gRPC 做内部服务通信
  • 消息批量发送(每 100ms 发一批,而不是每个事件一发)

前端

  • React 负责 UI 结构
  • WebGL(通过 regl)做高频图
  • 轻量 Canvas 做中频图
  • SVG(通过 D3)只用于低频、强交互的图

关键决策

  1. 尽量早聚合:前端应拿到「可直接展示」的数据,而不是原始事件。
  2. 区分更新频率:不是每个元素都要 10fps,静态上下文可以 30 秒一更。
  3. 让用户选粒度:在「总览」(更新慢、信息多)和「明细」(更新快、视野集中)之间可选。

性能优化

服务端

  • 预计算时间桶,别让客户端算「最近 5 分钟」
  • 增量编码:只发变化量,不发全量
  • 压缩:对 WebSocket 消息做 gzip,效果明显
  • 连接复用:多订阅共享连接

客户端

  • 对象池:复用图表元素,减少 GC
  • 用 RequestAnimationFrame 把更新对齐到浏览器渲染
  • Canvas 分层:静态一层、动态一层
  • Web Worker:在主线外解析入站数据

不推荐

  • 用 SVG 做高频更新(DOM 太慢)
  • 用 Redux 管实时状态(更新太频繁时开销大)
  • 用通用图表库做 >5fps(大多没为这类场景优化)

实时场景的 UX 教训

教训 1:把控制权交给用户

不是所有人都想要实时刷新,有人会觉得干扰。我们加了:暂停按钮、更新频率选择(1 秒 / 5 秒 / 30 秒)、历史模式(「显示 5 分钟前的状态」)。

教训 2:状态要一目了然

用户需要知道:当前是实时还是历史?上次更新是什么时候?连接是否正常?

我们加了一个随每次更新脉动的「心跳」指示,对安心感帮助很大。

教训 3:处理好「无聊」状态

大部分时间没有异常,图表只是用相近的值在更新。

这时要靠标注:「14:32 检测到尖峰」把注意力引到有意义的变动上,否则用户面对的是噪音。

教训 4:移动端要区别对待

屏幕小、网络差、要省电。移动端应:自动降低更新频率、简化图表、加强重连逻辑。

何时不必做「实时」

做完这套系统后,我对「实时」需求更谨慎了。要先问:

  • 更快更新会改变用户行为吗?
  • 用户真能在那幺快的节奏下行动吗?
  • 工程成本值吗?

对多数仪表盘,答案是否定的。15 秒刷新往往就够了,把「实时」留给「秒级真的重要」的场景。

工具与资源

层级 1–2(近实时、实时):

像 ChartGen 这类工具可以生成能有效轮询 API 的图表,配合 WebSocket 接口就能做出不错的实时仪表盘,无需自建整套基础设施。

层级 3(亚秒级):

需要专门方案:D3 + Canvas、自研 WebGL、或 uPlot、Apache ECharts 的增量更新模式等。

流式基础设施可选:

  • Apache Kafka + Flink(复杂但强大)
  • AWS Kinesis + Lambda(托管但有限)
  • Redis Streams + 自研聚合(简单但扩展性较弱)

如何监控实时系统

我们关注:端到端延迟(事件时间戳到像素上屏)、连接健康(每小时重连次数)、渲染性能(帧率)、用户参与(是否真的在看实时视图)。

一个发现:很多人打开仪表盘看两分钟就切到后台,「实时」数据大多没被看到。

最后一点

实时可视化既是工程挑战,也是 UX 挑战。最难的不是把数据尽快送到屏幕,而是以人类能理解和行动的方式呈现。

在做实时之前先问:用户有了更快的数据,到底会多做哪一步?

如果答案不清楚,可能根本不需要实时。

实时性能工程数据可视化规模

Ready to create better charts?

Put these insights into practice. Generate professional visualizations in seconds with ChartGen.

Try ChartGen Free