一行 Promise.all 争议:数据库查询并行真的没用?我和同事吵赢了!!!

最近因为下面一段代码和同事引发了一些讨论:

js 复制代码
// count 是一个异步函数,用于统计数据库中某个集合的数量
// find 是一个异步函数,用于查询数据库中某个集合的数据
await Promise.all([
    count(),
    find(),
]);

同事 :这段代码意图并行执行两个查询操作,但实际上起不到作用。因为两个查询共用一个数据库连接,所以两个查询是串行执行的。 :不对,两个查询操作是并行的。虽然共用一个连接,但是 Node 进程会在短时间内不等待 的发送两个查询请求到数据库服务,数据库会并行的执行两个查询操作,最后将结果返回,整体耗时减少了。 同事:我还是不理解。

上面是一个非常经典且重要的性能优化点。让我们一起来详细解释原因以及需要注意的事项。

1. 为什么并行执行更快

想象一下你在瑞幸小程序点单(瑞幸打钱!!!):

  • 串行执行

    1. 你点了杯冰美式。
    2. 你一直在等,直到冰美式做好。
    3. 冰美式订单完成后,你再点一杯生椰拿铁。
    4. 再等生椰拿铁做好。
    • 总耗时 ≈ 冰美式制作时间 + 生椰拿铁制作时间
  • 并行执行

    1. 你同时下单了冰美式和生椰拿铁。
    2. 咖啡店的店员同时制作这两杯子饮品。
    3. 你只要等待制作时间最长的那一杯咖啡完成(一般生椰拿铁制作时间会更长)。
    • 总耗时 ≈ 生椰拿铁制作时间

在上述的场景中,Node 服务就是你,而数据库就是"瑞幸"。

  • 串行调用Node 发送第一个 count 查询,然后等待 数据库返回结果。收到结果后,再发送第二个 find 查询,再等待结果返回。总耗时是两个查询执行时间的之和。
  • 并行调用Node 同时发送两个查询到数据库。数据库收到请求后,会根据内部调度机制(多进程、多线程)同时处理这两个查询。Node 等待两个查询都完成后返回结果(忽略异常情况)。总耗时是两个查询执行时间的最大值。

2. 代码示例

串行执行(稍慢)

js 复制代码
// 伪代码,只用于示例

const db = new DB();

async function getUsers() {
    const count = await db.count();
    const users = await db.find().limit(20).offset(0);

    return {
        count,
        users,
    };
}

并行执行(更快)

js 复制代码
// 伪代码,只用于示例

const db = new DB();

async function getUsers() {
    const [count, users] = await Promise.all([
        db.count(),
        db.find().limit(20).offset(0),
    ]);

    return {
        count,
        users,
    };
}

3. 注意事项

虽然并行执行是一种优化性能的方式,但也需要注意以下事项:

  • 数据库连接数:数据库通常会限制每个连接的最大并发查询数。如果并行执行的查询数量超过了数据库的连接数,就会导致连接池满,后续的查询会被阻塞。
  • 查询依赖:如果并行执行的查询之间存在依赖关系,比如第二个查询依赖第一个查询的结果,那么就不能并行执行。
  • 错误处理 :如果使用 Promise.all 发起并行调用,在其中任何一个异步调用发生错误时,会导致整个 Promise.all 失败,且不会等待其他异步操作完成。
  • 事务处理:如果并行执行的查询涉及到数据库事务,需要注意事务的隔离级别和并发控制机制,避免出现数据不一致的情况。

这里推荐一个 处理 Promise 的库,bluebird,它不仅兼容原生 Promise,也提供了丰富的 Promise 相关方法。如 Promise.map 方法可以并行执行多个异步操作,且可以控制并发数。

4. 特殊情况

假设数据库也是单进程的,这个时候并行和串行在总耗时上就没有区别了。但是一般数据库或服务都会采用线程池的机制,来处理多个并发请求。

相关推荐
YH丶浩2 小时前
vue自定义数字滚动插件
开发语言·前端·javascript·vue
阿民_armin2 小时前
Canvas 冷暖色分析工具
前端·javascript·vue.js
小岛前端2 小时前
大小仅 1KB!超级好用!计算无敌!
前端·javascript·开源
文心快码BaiduComate3 小时前
Comate分饰多角:全栈开发一个Python学习网站
前端·后端·python
道可到3 小时前
淘宝面试原题 Java 面试通关笔记 02|从编译到运行——Java 背后的计算模型(面试可复述版)
java·后端·面试
SimonKing3 小时前
GitHub 标星 370k!免费编程资源大合集,从此自学不花一分钱
java·后端·程序员
若水不如远方3 小时前
深入理解 Linux I/O 多路复用:从 select 到 epoll演进之路
linux·后端
自由的疯3 小时前
Java(32位)基于JNative的DLL函数调用方法
java·后端·架构
RoyLin3 小时前
SurrealDB - 统一数据基础设施
前端·后端·typescript