一行 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. 特殊情况

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

相关推荐
JustHappy4 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
老毛肚4 小时前
jeecg-boot-base-core 02 day
javascript·python
Hommy884 小时前
【剪映小助手】添加图片接口(Add Images)
后端·github·剪映小助手·视频剪辑自动化
GetcharZp5 小时前
别再盲目用 OpenCV 读图了,这才是 CV 预处理的终极杀手锏!
后端
IT_陈寒9 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端
烬羽9 小时前
后端返回的 JSON 字符串,浏览器怎么"看懂"的?——Ajax 全链路拆解
javascript
zhuxiaojt9 小时前
npx 为何如此之慢?浅谈 npx 速度慢的原因及工具推荐
node.js
椰椰椰耶10 小时前
[SpringCloud][14]OpenFeign参数传递方法
后端·spring·spring cloud
onething36510 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理
人工智能·后端
半个落月10 小时前
一个新手用 Bun + Axios 调通 DeepSeek API 的实践记录
javascript