后端一次传过来2000万条数据,前端怎么处理?

要可视化展示设备数据,而这个设备数据非常多,一小时就能产生上百万条数据,传过来的json文件都有几百兆大小;
我使用的vue3 vite echarts chrome单标签4g内存爆了

使用原生html js echarts,然后直接引入这个json文件到渲染出来要20秒左右,但是能够展示

不能取平均值等,降低采样来减小吗?

我们分了一个降采样的查询和不做任何处理的查询,这个就是那个不做处理的

image.png
原生js

<div id="main" style="width: 600px;height:400px;"></div>
<!-- <div id="tester" style="width:600px;height:250px;"></div> -->
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<!-- <script src="./plotly-2.32.0.min.js" charset="utf-8"></script> -->
<script src="./response.js"></script>
<script>
  var chartDom = document.getElementById('main');
  var myChart = echarts.init(chartDom);
  var option;
  const xx = data.data.dates
  const yy = data.data.directionx
  option = {
    xAxis: { type: 'category', data: xx },
    yAxis: { type: 'value' },
    series: [{ data: yy, type: 'line', sampling: 'lttb' }]
  };
  option && myChart.setOption(option);
</script>

部分数据:

{
  "code": 0,
  "msg": null,
  "data": {
    "directionx": [6.492834006493665E-4, 6.509656666877379E-4,],
    "directiony": [5.452070495968005E-4, 5.45381096391632E-4,],
    "dates": ["2024-07-11 13:43:00", "2024-07-11 13:44:00",],
    "directionz": [5.630059477387331E-4, 5.6277585267251E-4,],
    "info": ["[2,+)", "[1.6,2)", "[1.2,1.6)"]
  },
  "ok": true
}

5个小时的数据:123云盘
vue方面我使用的vue-echarts

希望各位多给点建议或者评论,各个方面都可以

目前有的想法是:
1.iframe (尝试中)
2.流式处理,一次处理一部分,这样应该不会爆内存(没想好具体怎么实现)
3.流式传输,大范围时只有部分数据(反正像素点都被画满了,少了数据看起来也是一样的)当范围缩小时,再进一步拉取数据,类似与地图瓦片那种

尝试使用iframe,把数据传过去(想省去登录),但是加载不出来,但是如果只使用少量的条数就可以,很奇怪,说明东西已经拿到只差渲染了,原生js不是能成功吗?

<iframe id="myIframe" src="../chart/echart.html"></iframe>
  <script src="./response.js"></script>
  <script>
    const iframe = document.getElementById('myIframe');
    iframe.onload = function () {
      iframe.contentWindow.postMessage(data, '*');
    };
  </script>
<div id="main" style="width: 600px;height:400px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script>
  var chartDom = document.getElementById('main');
  var myChart = echarts.init(chartDom);
  let option;
  window.addEventListener('message', function (event) {
    const data = event.data;
    document.getElementById('title').innerHTML = '123';
    const xx = data.data.dates.slice(0,1000)
    const yy = data.data.directionx.slice(0,1000)
    option = {
      xAxis: { type: 'category', data: xx },
      yAxis: { type: 'value' },
      series: [{ data: yy, type: 'line', sampling: 'lttb' }]
    };
    option && myChart.setOption(option);
  });
</script>
阅读 7.3k
avatarAI BotBETA

回答

处理大量数据,尤其是在前端,通常需要采用一些策略来避免内存不足和性能问题。对于你提到的情况,以下是几个建议:

1. 流式处理或分块加载

  • 正如你提到的,流式处理或分块加载是一种有效的策略。你可以将后端返回的数据分割成多个小块,并在需要时逐个加载和渲染。
  • 你可以在后端设置API来返回数据的一个子集(例如,基于时间戳的范围或分页),并在前端根据需要请求这些数据。

2. 使用Web Workers

  • Web Workers 允许你在浏览器后台线程中运行JavaScript,从而避免阻塞UI线程。你可以将数据处理逻辑放入Web Worker中,以减少对主线程的影响。

3. 虚拟滚动/渲染

  • 对于大量数据的可视化,通常不需要一次性渲染所有数据点。你可以使用虚拟滚动或虚拟渲染技术来仅渲染当前可见的数据点。当用户滚动或缩放视图时,可以动态地加载和渲染新的数据点。

4. 数据聚合/下采样

  • 尽管你提到不能降低采样或减小数据,但在某些情况下,对数据进行聚合或下采样可能是必要的。这可以通过计算平均值、最大值、最小值等来减少数据点的数量,同时保留数据的整体趋势。

5. 使用更适合大数据可视化的库

  • ECharts 是一个非常强大的可视化库,但它可能不是处理大量数据的最佳选择。你可以考虑使用其他专为大数据可视化设计的库,如 Deck.gl、Three.js 结合 Plotly.js 等。

6. 优化数据传输

  • 尽量减少数据传输的大小。考虑使用更有效的数据格式(如GZIP压缩的JSON),或仅传输必要的数据(而不是整个JSON文件)。

7. 使用服务器端渲染

  • 如果可能的话,考虑使用服务器端渲染来生成图像或图表,并将其作为静态文件或图像发送到前端。这样,前端就无需处理大量数据或执行复杂的渲染逻辑。

8. 关于使用iFrame的建议

  • 使用iFrame时,请确保你正确地处理了跨域问题(如果适用)。此外,iFrame可能会增加页面加载时间和复杂性,因此请仔细评估其是否适合你的用例。

9. 其他注意事项

  • 考虑在前端实施内存管理和清理机制,以确保不再需要的数据被及时释放。
  • 监控和测试你的应用程序的性能和内存使用情况,以便及时发现并解决潜在问题。

希望这些建议能对你有所帮助!

7 个回答

可以30s为一个刻度,让后台统计每个30秒的均值,图表中默认以30s甚至几分钟一个刻度展示,如果用户放大x轴想看某几分钟内的详情,在让后台返回某个时间段内的所有数据。总之就是提前计算好和只拿需要的数据。
第二个点就是数据传输格式了,可以不用json格式。从你的逻辑可以看出,需要的只是时间点(datas)和时间点对应的浮点值(directionx),那么是否可以用long保存时间戳,double保存浮点值。这样一对数据是16字节,200万对数据的大小是2000000*16/1024/1024 = 30M。你如果还嫌大,并且容忍精度误差,可以用float和int,这样就是15M。
如果是这样传递,你可能需要借助ArrayBuffer和DateView对象提取出来long和double。如果取出来的数不对就考虑一下字节序。

如果你directionx中数据一定是时间连续的,每一分钟都有一条记录的话,那么你的数据包中只需要有一个起始时间和时间间隔就可以了。如果你的directionx数据不连续,但只有极少数时间点缺失。可以通过,时间间隔,多组的【起始时间,结束时间】来表示。

如果你想要做持续推送更新,可以用EventSource,但是注意EventSource传递的是字符串,所以刚刚说的数据格式在发送前可以做一次base64编码,你页面端再做一次base64解码。但数据传输量会是原来的1.33倍。

我有一个想法,让后端把每一个小时的数据提取出较大差值的峰值,显示出来就可,反正看这种图表,看峰值的较多。这样一下就省去了许多条数据量。

来个偏门做法:

如果你愿意引入 7 MB (gzip)duckdb 模块,

你可以将这 240 MB 数据,转成 50 MB 的数据库,

前端花 0.5 s,将任意时间范围、时间间隔,降采样数据后,再展示出来。

转换方法

(约 8 秒。若用 csv 会更快,也支持 stdin 流式读取)

./duckdb data.db '
    CREATE TABLE data (
        dates DATETIME,
        directionx DOUBLE,
        directiony DOUBLE,
        directionz DOUBLE
    );

    INSERT INTO data (directionx, directiony, dates, directionz)
    SELECT unnest(COLUMNS(data.* EXCLUDE info))
    FROM read_json("response.json", maximum_object_size=256<<20);
'

前端查询截图

把后端打一顿,让他做分页。2000 万条数据前端不可能处理的了的。

从传输的角度,可以精简下格式,甚至不一定是 json,把大小降下来,假如 200M,gzip 先降到 60M,再通过精简或者压缩方法,可能可以更小。

从渲染角度,可能你的数据是 reactive 的,所以内存爆了?你可以尝试下不 reactive 试试。

首先,这么大的数据量就不应该用 JSON。JSON 的格式要求太严格了,不利于分块处理。

建议用纯文本,换行符+制表符分割,类似 csv 的格式,这样开发调试都比较容易。

然后用流式传输,边接受边解析,每次只处理一部分,降低前端压力。

最后,适当压缩数据,我猜测大部分数据其实都不需要发给前端,后端适当合并计算会好很多。

推荐问题
宣传栏