1 min read
node.js性能优化实践记录

最近由于业务需要,涉及到了数据批量增删改查的逻辑,在数据较多的时候接口的处理时间变得非常长,于是我又开始了一年一度的性能优化之旅。

由于处理的数据是链表树结构,考虑到新节点在创建时还没有 id,刚开始的操作是通过递归的方式当 parent 保存后,再拿返回的 id 和当前层级的节点信息一起保存,算法本身的时间复杂度为 O(n),由于存在依赖关系,从逻辑上无法并发处理。

我的目标是把 IO 操作的时间复杂度降低到 O(1),为了解决对新保存的节点 id 的依赖,我在 flat 节点的时候同时通过 code 存下了当前节点的层级关系,然后通过 Promise.all 同时保存所有的节点,拿到所有节点的 id 后再按之前记录的 code 规则创建一个 code=>id 的 map,再同时更新所有节点的层级信息。

虽然处理的时间已经从 10s 左右降到了 2s 左右,但是我发现节点数量的多少依然会对返回时间有比较大的影响。按我对 node.js 异步操作的理解,在发起请求并推入微任务队列后,应该只会由最长的那次操作影响最后的返回时间,为什么会有这么大的差异呢?

我开始 debug 大数据情况下服务运行时的堆栈情况,竟然发现了非常多零散的 idle,创建异步请求并推入 misrotask 并不是无缝的。

在 Node.js 中创建大量 Promise 主要会带来以下开销:

  1. Promise 本身的创建开销
  • Promise 是 JavaScript 对象,创建它们需要分配内存、初始化状态和回调队列等。
  • 在 V8 中,Promise 是一个 JSReceiver(JS 对象),其内部存储状态 (PromiseState)、结果 (PromiseResult)、回调列表 (PromiseFulfillReactions、PromiseRejectReactions)。
  • 由于 JS 是垃圾回收语言,如果创建大量 Promise,会增加 GC 压力,导致不必要的垃圾回收停顿。