800条/秒数据写入Electron,我是怎么做到的?(上篇)

——先拆了这三颗雷

有朋友问我:“你们那个Electron脚手架,吹什么800条/秒写入,骗鬼呢?Electron里搞数据采集,不卡成PPT就谢天谢地了。”

我笑了笑,没反驳。

因为他说的没错——如果用市面上那些“傻瓜式”的配置,别说800条,80条都能让你体验什么叫“时光倒流”。界面卡死、CPU飙红、数据库写入像便秘。

但我的脚手架里,从来不放傻瓜配置。

我今天就把底裤扒给你看,这800+是怎么“调教”出来的。文章分上下两篇,上篇先拆三颗最隐蔽的地雷

为什么“默认配置”一跑就崩?

先说说大部分Electron项目是怎么死的。

假设你接到一个活儿:做一个工业数据采集软件,串口每秒来200条数据,要实时显示、存文件、写数据库。

你打开Electron+Vue,吭哧吭哧写完,一跑——完蛋

原因很简单,你掉进了三个坑:

现象 真实原因
坑1 界面卡成幻灯片 数据处理在主线程,阻塞了Vue渲染
坑2 数据库写入巨慢 每条数据一个事务,刷盘刷到怀疑人生
坑3 文件写入拖后腿 每条数据一次write()系统调用,频繁切换内核态

上篇我们先炸坑1。坑2和坑3在下篇收拾。

解法一:Web Worker——让数据生产和UI渲染“分家”

你原来的写法(伪代码):

// 主线程直接处理所有事
serialPort.on('data', (data) => {
  saveToDatabase(data);  // 等待数据库写入
  updateUI(data);        // 等待DOM更新
  saveToFile(data);      // 等待文件写入
  // 下一个数据被堵在外面
});

问题:串口来数据时,你的主线程正在等数据库写入完成,界面卡死,下一个数据又来了——雪崩。

我的解法:

// serial-worker.js — 运行于独立线程
function generateLoop() {
  const deltaTime = performance.now() - lastTime;
  accumulator += deltaTime;
  const shouldGenerate = Math.floor(accumulator / interval);

  if (shouldGenerate > 0) {
    const batch = [];
    for (let i = 0; i < shouldGenerate; i++) {
      batch.push(generateMockData());
    }
    // 一批数据一次性发给主线程
    self.postMessage({ type: 'data-batch', data: batch, count: shouldGenerate });
  }
  timer = setTimeout(generateLoop, 0);
}

核心收益

  • Web Worker独立线程,数据生成/预处理不阻塞Vue渲染

  • 使用performance.now()高精度时钟+累加器算法,长时间运行不漂移

  • postMessage结构化克隆,零拷贝传输

➡️ 收益:渲染帧率稳定60fps,数据生产永不阻塞UI

解法二:双缓冲队列——接收和写入“离婚”

你原来的写法:

// 来一条,写一条
async function onDataReceived(data) {
  await db.insert(data);   // 等待写完
  await file.append(data); // 等待写完
  // 耽误了下一条数据的接收
}

我的解法:

// 接收到数据后,非阻塞地投入队列
function handleDataBatch(batch, count) {
  // 扔进文件队列,不等写完
  if (config.saveToFile) {
    saveQueue.value.push(...batch);
    if (saveQueue.value.length >= 200) {
      processSaveQueue(); // 异步,不await
    }
  }

  // 扔进数据库队列,也不等写完
  if (config.saveToDatabase) {
    dbQueue.value.push(...batch);
    if (dbQueue.value.length >= 1000) {
      processDbQueue(); // 异步,也不await
    }
  }
}

设计思路

队列 批次大小 目标存储 触发条件
文件写入队列 200条/批 fs.WriteStream 队列积压≥200条
数据库写入队列 1000条/批 SQLite 队列积压≥1000条

➡️ 收益:接收逻辑不等待任何I/O,数据一来自动入队,零阻塞。

阶段性成果:加了这两招能跑多快?

加上Web Worker和双缓冲队列后:

  • ✅ UI不再卡死,帧率稳定

  • ✅ 数据接收不再被I/O阻塞

  • ⚠️ 写入速度:约200-300条/秒

离800+还差得远。

因为剩下的瓶颈在哪?在文件写入数据库写入——这两个懒鬼,来一条写一条,从来不攒一攒。

怎么让它们学会“攒批”?怎么用SQLite事务和流式写入再往上冲5倍

下篇预告:最后三刀,从200飙到800+

下篇我会拆:

  1. 文件流批量写入:500条合并成1次系统调用,吞吐提升50倍

  2. SQLite事务批量写入:1000条只触发1次磁盘同步,吞吐提升1000倍

  3. UI渲染节流:10fps够用了,别逼界面拼命刷新

P.S. 如果你现在就等不及,想看看成品长什么样——

*👉 对了,这个脚手架我自己用着挺爽,也挂在闲鱼上补贴点咖啡钱。如果你觉得文章有帮助,想去看看的话,搜【小猫吃咸鱼2020】就能找到我。

哪怕不买,点个“我想要”跟我聊两句技术,我也欢迎。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容