我们这边有一个接口,里面会做很多的查询操作,会查各种表,单个表的查询其实并不慢,但是一旦查询的次数多了,加起来就会慢。 例如查询一个用户的信息,会连带着查相关的信息,这些信息保存在其他表里,那么现在想并发的查询这些账号的信息。 然后查好后再合并到一起。
await Promise.All([查询1,查询2]) 试试?
可试试:
- 连表查询,做好索引优化
- 分表(并发)查询,然后在应用层聚合
@dbit-xia 我试了下,类似于下面这样,查询语句是按照,4、12、13、15的顺序运行的。
await Promise.all([4,12,13,15].map(async(userId) => {
await services.user.findUser(userId);
}))
@waitingsong 关于第2点,我现在在尝试用 Worker threads 并发查询
@pwstrick 为啥要在 worker 作并发呢。 Promise.all 也行的,查询压力在数据库和网络io,当前进程又不会有啥压力。
@waitingsong 因为我试下来,数据库语句还是按顺序在查,我想要的效果同时查数据库
@pwstrick 试试不要await,而是直接返回promise await Promise.all([4,12,13,15].map((userId) => { services.user.findUser(userId); }))
@zhennann 这样
await Promise.all([4,12,13,15].map((userId) => {
return services.user.findUser(userId);
}))
await 被滥用的地方之一, https://zhuanlan.zhihu.com/p/75946868)
const waitTime = async (time) => {
return new Promise((res) => {
setTimeout(() => res(time), time)
});
}
(async ()=>{
console.time('串行')
await waitTime(2000)
await waitTime(1000)
console.timeEnd('串行')
console.time('并行')
await Promise.all([waitTime(2000), waitTime(1000)])
console.timeEnd('并行')
})()
@zhennann @waitingsong 我尝试了下两种写法,第一种是你们说的,省略了async
const start1 = Date.now();
const userList1 = await Promise.all( [4, 12, 13, 15, 18, 21, 22, 23, 24, 26].map((userId) => {
console.log(Date.now(), userId);
return services.user.findUser(userId);
})
);
const end1 = Date.now();
从打印的结果中,可以看到并发。
1663831219351 4
1663831219351 12
1663831219351 13
1663831219352 15
1663831219352 18
1663831219352 21
1663831219352 22
1663831219353 23
1663831219353 24
1663831219354 26
然后我又将async加上。
const start2 = Date.now();
const userList2 = await Promise.all([27, 28, 29, 30, 31, 32, 33, 34, 35, 36].map(async (userId) => {
console.log(Date.now(), userId);
return services.user.findUser(userId);
})
);
const end2 = Date.now();
从打印的结果中,也可以看到并发。
1663831219575 27
1663831219575 28
1663831219575 29
1663831219576 30
1663831219576 31
1663831219576 32
1663831219577 33
1663831219577 34
1663831219577 35
1663831219578 36
在代码最后,我还打印出了两者的执行时间
console.log('第一种', end1 - start1, userList1.length);
console.log('第二种', end2 - start2, userList2.length);
发现前者没有后者高效。
第一种 224 10
第二种 59 10
之前也尝试了Worker threads,发现这玩意儿传递的对象中,其属性值不能是函数,一但有函数,就会报错。
@pwstrick 函数体中没有await语法,函数名前面加不加async都一样。两者时间不一样,跟你执行的先后有关系。多加几个循环,反复执行,再比较二者的时间效率
@pwstrick 你那个第二种,很有可能是数据库已经缓存了第一次的查询结果了。所以数量级差距这么大
看你用的什么数据库。如果是mongodb这种内存型数据库,并发查询相对于一个一个查询是肯定会快很多。 mysql 这种即使并发可能效果并不是很好,mysql这种io开销比较高。 建议使用:
Promise.allSettled([search1, search2,...])
Promise.all的缺点是其中一个reject掉,整个都会reject掉。 另外,如果查询个数 比较大了,建议控制并发数。
const mockTask = async (num) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num)
}, 3000)
})
}
const searchList = [...new Array(100).keys()]
const limit = 10 // 任务并发数
let list;
async function main() {
while ((list = searchList.splice(0, limit)) && list.length) {
const result = await Promise.allSettled(list.map(n => mockTask(n)))
console.log(result.map(item => item.value).toString())
}
}
@zhennann @cd-xulei 我对两段代码调换了顺序,放在前面会比较慢,不过当我连续执行时,两者的时间类似,看来和执行顺序是有点关系的。 我会用到两种数据库 MongoDB 和MySQL,大部分是 MySQL 的业务。 我还想到了一种优化手段,就是之前一条一条的查询:
select * from user where id = 4;
select * from user where id = 12;
select * from user where id = 13;
select * from user where id = 14;
改成批量查询:
select * from user where id in (4,12,13,14);
拿到结果后,在脚本代码中整合。