想了解下并发查询数据库是否可行
发布于 10 天前 作者 pwstrick 990 次浏览 来自 问答

我们这边有一个接口,里面会做很多的查询操作,会查各种表,单个表的查询其实并不慢,但是一旦查询的次数多了,加起来就会慢。 例如查询一个用户的信息,会连带着查相关的信息,这些信息保存在其他表里,那么现在想并发的查询这些账号的信息。 然后查好后再合并到一起。

16 回复

await Promise.All([查询1,查询2]) 试试?

可试试:

  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);

拿到结果后,在脚本代码中整合。

回到顶部