实时数据的幻觉:你看到的"现在"其实是"过去"

每个产品经理都想要"实时"。实时报价、实时持仓、实时通知、实时风控——但凡跟"实时"沾边的需求,优先级都高得离谱。好像只要数据不是实时推送的,这个功能就不值得做。

但这里有个问题少有人追问:你看到的"实时",到底实时到什么程度?

我见过太多团队在"实时"这两个字上栽跟头。不是技术做不出来,是做出来了才发现——用户根本分不清实时和准实时的区别,而团队却为那500毫秒的"实时"付出了整个架构的代价。

 

WebSocket的承诺和账单

 

先从最常见的"实时"方案说起。

WebSocket火了之后,几乎每个前端团队都想把长连接加上。HTTP轮询?那是不体面的技术债务。SSE?那是不完整的实时。只有全双工的WebSocket才配得上"实时"这两个字。

但WebSocket不是免费的午餐,它的账单分三层。

第一层是连接管理。一个WebSocket连接意味着服务端要维护一条持久化的TCP通道。听起来不复杂,但你算一下:10万个在线用户,10万条长连接,每条连接都有心跳、超时、重连逻辑。你的服务端不再是无状态的HTTP服务器了,它变成一个有状态的连接管理中心。这个转变的代价远比大多数团队预估的大。

第二层是状态同步。连接建立后,你在前端维护了一份服务端状态的"投影"。投影这个词很关键——它意味着你本地的状态永远是服务端状态的某个历史版本。用户看到的数据,严格来说,是某个时间点的快照。你推得再快,从服务端状态变更到前端视图渲染,中间至少经过:事件生成、序列化、网络传输、反序列化、状态更新、虚拟DOM diff、DOM更新。这一整条链路下来,50毫秒算快了。可50毫秒前,服务端的状态可能已经又变了。

第三层是容错。网络断开怎么办?重连后状态怎么补偿?并发更新冲突怎么处理?消息丢了怎么办?顺序乱了怎么办?这些问题在HTTP世界里根本不存在——每次请求都是独立的、幂等的。但WebSocket把一个本来简单的请求-响应模型变成了一个复杂的事件驱动模型,你得自己处理所有这些边界情况。

我见过一个理财产品的实时持仓功能,光WebSocket重连和状态补偿的逻辑就占了前端代码的40%。40%的代码在处理"实时"的边界情况,真正展示持仓数据的逻辑只有几百行。

 

"实时"是个光谱,不是开关

 

很多人讨论实时数据的时候,把它当成一个二元问题:要么实时,要么不实时。这种思维特别有害。

实际上,"实时"是一个光谱。我们来看几个典型的数据时效性等级:

强实时(< 100ms):股票行情推送、竞拍出价更新。这种场景下,毫秒级的延迟可能导致用户做出错误决策。但即便如此,交易所到券商到终端的链路延迟,普通用户能拿到的"实时"行情本身就有几百毫秒的延迟。你以为你在看实时行情,其实你在看几百毫秒前的快照。

准实时(100ms - 5s):持仓更新、交易状态变更、消息通知。绝大多数金融App里的"实时"数据,其实落在这个区间。用户感知不到1秒延迟和100毫秒延迟的区别,但架构复杂度的差异是数量级的。

近实时(5s - 30s):资产汇总、收益计算、风控指标。这些数据通常需要多方聚合计算,即使底层有实时推送,最终呈现给用户的也必然是延迟的。

批量更新(> 30s):历史收益、统计报表、合规数据。这些数据本质上就是T+1或小时级的,强求实时毫无意义。

问题来了——大多数产品需求里的"实时",落在准实时和近实时区间。而大多数团队的第一反应,是上WebSocket,做强实时方案。这不是在解决问题,这是在过度工程化。

 

金融场景里的"实时"陷阱

 

金融业务对"实时"有一种执念。这个执念部分合理——毕竟价格数据确实需要及时。但很多时候,合理的诉求被无限放大了。

举个例子。理财产品有个常见需求:用户买入后,"实时"看到持仓。逻辑上听起来很简单——成交了就推消息,前端更新持仓列表。

但实际链路是:交易系统确认成交 → 资金系统扣款 → 清算系统入账 → 持仓系统更新 → 消息推送 → 前端收到 → 渲染。这里每一步都有延迟,有些步骤还有批量处理的窗口。从用户下单到看到持仓,正常走完可能要几秒到几十秒。

这时候,产品经理说:"我们要做到实时!"

怎么做?有两种思路。

一种是在成交确认后,前端直接乐观更新——先假设成交成功,给用户一个"你的持仓已更新"的幻象,后台慢慢确认。这在很多场景下没问题,但在金融场景下,乐观更新是危险的。万一资金不足?万一限额了?万一交易失败了?你给用户展示了一个还没确认的持仓,这就是合规风险。

另一种是缩短链路——做一个专门的查询接口,成交后前端轮询。这本质上就是在拉长轮询间隔和用户 patience 之间找平衡。

两条路都不完美。但关键不是哪条路更好,而是——用户真的需要"实时"看到持仓吗?买入一个定期理财,用户打开App的频率可能是一天一次。给一个30秒内刷新的持仓状态,和给一个"实时推送"的持仓状态,用户体验的差异几乎为零。

但架构复杂度的差异是实实在在的。

 

推送 vs 拉取:一个不该有标准答案的问题

 

技术圈有个不成文的鄙视链:推送 > 短轮询 > 长轮询。好像用了轮询就不是"真正的工程师"。

但现实是,轮询在很多场景下是更优的选择。

想想你的页面打开了多少个WebSocket连接。每个连接都在消耗服务端资源,每个连接都在可能断线,每个连接都需要维护状态。而轮询呢?一个HTTP请求,拿到数据,结束。无状态、幂等、天然可重试。

更关键的是,轮询的节奏是你自己控制的。你可以在用户活跃时1秒轮询一次,在用户闲置时30秒轮询一次,在页面不可见时停止轮询。这种自适应的策略比一个永远在线的WebSocket更经济,也更可靠。

我见过一个估值平台的做法:行情数据用WebSocket推送(因为变化频繁且用户确实关注实时性),但持仓统计用5秒间隔的轮询。这个团队做过A/B测试,用户对持仓统计的感知完全一样——无论5秒轮询还是WebSocket推送,用户都"觉得"是实时的。

反而用WebSocket的版本,在网络不稳定时出现了更多问题——数据断档、状态不一致、偶尔闪一下又消失的持仓数据。这些bug比5秒延迟的伤害大得多。

"用户觉得是实时的"和"技术上确实是实时的",中间差了几个数量级的工程成本。

 

那些被忽略的更诚实方案

 

与其追求绝对实时,不如让数据透明地展现它的时效性。

展示最后更新时间。这不是什么高深的策略,但几乎没有产品愿意做。为什么?因为一旦展示了"最后更新:3秒前",用户就会意识到数据可能有延迟。这打破了"实时"的幻觉,产品经理不喜欢。

但这就是正确做法。在金融产品里,一条"最后更新:5秒前"的时间戳,比一个假装实时但偶尔出错的推送要负责任得多。证券交易所的终端都会在数据流上打时间戳,不是没有道理的。

优雅降级而非硬撑。网络断了就断了,展示一个清晰的"连接中断,正在重连"状态,比假装数据还有效强得多。很多产品在断连时不愿意显示任何提示,因为怕"影响体验"。但真正影响体验的是:用户根据过时数据做了决策,事后发现数据是错的。

分层更新策略。核心数据用推送(比如交易方向确认),辅助数据用轮询(比如统计汇总),展示数据用缓存(比如历史走势)。不是所有数据都需要同等的时效性,分层处理比一刀切的"全量实时"更合理也更经济。

 

事件驱动架构的诱惑和代价

 

说到实时,很多后端团队会第一时间想到事件驱动架构(EDA)。让所有状态变更都以事件的形式广播出去,前端只管订阅感兴趣的事件。

听起来很美。但事件驱动架构有一个很少被讨论的副作用:它把耦合从接口层面转移到了事件层面。

以前你改一个接口,影响范围是明确的——谁调用了这个接口,谁受影响。但在事件驱动架构里,改一个事件的字段,你很难知道下游有多少消费者在依赖这个字段。事件是隐式契约,没有强类型的接口约束,重构起来极容易出问题。

更麻烦的是事件顺序。在一个分布式系统里,A服务和B服务同时发出事件,前端收到的顺序可能和发生顺序不一致。你以为用户先看到"下单成功"再看到"持仓更新",实际上可能先收到持仓变更的事件,再收到下单成功的事件。这时候前端的状态管理就变得极其复杂——你需要自己维护事件的时间线,处理乱序、去重、补偿。

还有事件的膨胀。一个交易流程可能产生十几个事件:下单、风控通过、资金冻结、成交确认、清算入账……前端到底该订阅哪些?订阅少了会漏状态,订阅多了会处理爆炸。到最后,很多团队的做法是在事件总线之上再架一层聚合服务——把分散的事件重新聚合成一个"前端友好"的模型。这一层聚合服务的复杂度,往往比直接提供一个查询接口高得多。

我观察到一个规律:推动事件驱动架构的通常是后端团队,因为事件驱动对后端的异步处理和解耦确实有帮助。但承受复杂度的往往是前端团队——他们要处理事件乱序、状态同步、错误恢复这些本来不需要操心的问题。这和BFF的问题异曲同工:架构上的优化,成本被转移了。

 

什么时候应该追求真正的实时

 

说了这么多实时的坏话,不是要否定实时技术的价值。在某些场景下,真正的实时确实是刚需。

交易场景。买入卖出的确认、成交回报——这些信息延迟一秒都可能影响用户决策。在这种场景下,WebSocket甚至更底层的长连接是必要的。但注意,这里的"实时"是针对特定的事件(成交确认),不是全量数据实时。

协同编辑。多人实时协作的场景,对冲突管理和状态同步有硬性要求。但协同编辑的实时方案本身就是一个专门领域,不是简单上个WebSocket就能解决的。

实时监控。风控大盘、系统健康度、关键指标告警——这些场景需要快速感知变化,延迟越小越好。但这类场景通常是内部系统,用户也是专业人员,对数据异常的容忍度反而更低——他们需要的是准确和及时,假装实时不如标明延迟。

除此之外,大部分场景的"实时"需求都可以降级为准实时甚至近实时,而不会真正影响用户体验。差异只存在于产品文档里,不存在于用户的心智中。

 

与其追逐实时,不如管理预期

 

回到核心观点:"实时"是一个非常昂贵的承诺,而大多数产品并不需要兑现这个承诺。

真正伤害用户的不是延迟,是信息不对称。当你的界面看起来像实时,但数据其实是5秒前的,你在误导用户。当连接断了但界面没有任何提示,你在欺骗用户。当多个数据源的时间基准不一致导致页面闪烁着矛盾的数据,你在消耗用户的信任。

与其花大量工程资源去追求50毫秒的实时性,不如花少量资源做好三件事:告诉用户数据是什么时候更新的、告诉用户数据可能不准确、在数据确实不准确的时候给出明确提示。

这三件事的技术实现比WebSocket简单几个数量级,但对用户的价值——尤其是在金融这种对数据准确性高度敏感的领域——反而更大。

实时数据是工程能力的体现,但诚实地展现数据的时效性是产品能力的体现。前者考验你的技术,后者考验你对用户的尊重。

在"实时"这个需求上,多数团队的问题不是做不到,而是不该做。只是没人愿意承认,"够用"比"实时"更难说服利益相关者。

但"够用"才是大多数场景下的正确答案。你在客户端看到的每一个"实时"数据,背后都有一个你不知道的时间差。重要的不是消除这个时间差,是让用户知道它存在。

Total votes: 8

添加新评论