Node.js 获取连接超时。游泳池可能已经满了。更新10000行时出错

Node.js 获取连接超时。游泳池可能已经满了。更新10000行时出错,node.js,postgresql,knex.js,Node.js,Postgresql,Knex.js,当我尝试使用knexjs更新5000行时,获取连接时会出现错误超时。游泳池可能已经满了。” 当我查看CPU使用率时,我发现postgres pids总是占用90-98%的CPU使用率,这是不正常的,我在每个kenx上都尝试了destroy(),但它破坏了连接,没有解决问题 这是我正在使用的代码 const knexDb = knex({ client: 'pg', connection: { host : '127.0.0.1', user : process.env.DB_US

当我尝试使用knexjs更新5000行时,获取连接时会出现错误超时。游泳池可能已经满了。”

当我查看CPU使用率时,我发现postgres pids总是占用90-98%的CPU使用率,这是不正常的,我在每个kenx上都尝试了destroy(),但它破坏了连接,没有解决问题

这是我正在使用的代码

const knexDb = knex({ client: 'pg', connection: {
    host : '127.0.0.1',
    user : process.env.DB_USER,
    password : process.env.DB_PASSWORD,
    database : process.env.DB_DATABASE,
    port: process.env.DB_PORT
  }});

arrayWith5ThousandObj.map(data => {
    knexDb('users').where({
      user: data.user,
    })
    .update({
      product: data.product
    })
    .catch(err => console.error('update user products', err))
})
这是一个每1分钟重复一次的循环函数,我也尝试了
.finally->knexDb.destroy()
,但它破坏了连接,我得到的错误是无法获得连接

我想使用knexjs不断更新5000行或更多行,比如10000+行,我认为PostgreSQL可以处理这个每分钟10秒数千次查询的大型网站,而不会出现问题。问题不在服务器上,因为服务器有10个CPU和16gb的RAM,所以资源不是问题,我停止运行pro除此应用程序外,服务器上的访问。postgres pid几乎完全不使用CPU。因此,出现了大量查询中的问题。是否有批量更新,我可以使用knexjs一次性更新所有10000+行

我最近尝试过这个解决方案

return knexDb.transaction(trx => {
    const queries = [];
    arrayWith5ThousandObj.forEach(data => {
        const query = knexDb('users')
            .where({
              user: data.user,
            })
            .update({
                product: data.product,
            })
            .transacting(trx); // This makes every update be in the same transaction
        queries.push(query);
    });

    Promise.all(queries) // Once every query is written
        .then(trx.commit) // We try to execute all of them
        .catch(trx.rollback); // And rollback in case any of them goes wrong
});
但我得到了这个错误:

{ error: deadlock detected
   at Connection.parseE (/*********/connection.js:601:11)
   at Connection.parseMessage (/*********/connection.js:398:19)
   at Socket.<anonymous> (/**********/connection.js:120:22)
   at Socket.emit (events.js:189:13)
   at addChunk (_stream_readable.js:284:12)
   at readableAddChunk (_stream_readable.js:265:11)
   at Socket.Readable.push (_stream_readable.js:220:10)
   at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
 name: 'error',
 length: 340,
 severity: 'ERROR',
 code: '40P01',
 detail:
  'Process 9811 waits for ShareLock on transaction 443279355; blocked by process 9808.\nProcess 9808 waits for ShareLock on transaction 443279612; blocked by process 9811.',
 hint: 'See server log for query details.',
 position: undefined,
 internalPosition: undefined,
 internalQuery: undefined,
 where: 'while locking tuple (1799,4) in relation "users"',
 schema: undefined,
 table: undefined,
 column: undefined,
 dataType: undefined,
 constraint: undefined,
 file: 'deadlock.c',
 line: '1140',
 routine: 'DeadLockReport' }
{错误:检测到死锁
在Connection.parseE(/*********/Connection.js:601:11)
在Connection.parseMessage(/*********/Connection.js:398:19)
在套接字上。(/*********/connection.js:120:22)
在Socket.emit(events.js:189:13)
在addChunk(_stream_readable.js:284:12)
在readableAddChunk(_stream_readable.js:265:11)
在Socket.Readable.push(_stream_Readable.js:220:10)
在TCP.onStreamRead[as onread](internal/stream_base_commons.js:94:17)
名称:“错误”,
长度:340,
严重性:“错误”,
代码:“40P01”,
详情:
'进程9811等待事务443279355上的ShareLock;被进程9808阻止。\n进程9808等待事务443279612上的ShareLock;被进程9811阻止。',
提示:“有关查询详细信息,请参阅服务器日志。”,
位置:未定义,
内部位置:未定义,
internalQuery:未定义,
其中:'在锁定与“用户”相关的元组(1799,4)时,
模式:未定义,
表:未定义,
列:未定义,
数据类型:未定义,
约束:未定义,
文件:“deadlock.c”,
行:“1140”,
例程:“死锁报告”}
用于控制并发:

knex.transaction((trx) => {
    Bluebird.map(arrayWith5ThousandObj, (data) => {
            return trx('users')
                .where({
                    user: data.user,
                })
                .update({
                    product: data.product,
                }))

    }, { concurrency: 5 })
    .then(trx.commit);
})
.then(() => console.log('all done'));

在初始解决方案中,您一次生成5000个承诺,所有这些承诺都尝试一次连接到数据库。此解决方案将确保最多有X个并发承诺,并且不会使用延迟,您可以微调解决方案的数量。Knex默认为连接。

Knex对于此类大型bat来说并不是一个正确的工具h更新。特别是在你使用它的方式上,它是特别无性能的

在初始化5k查询生成器时,所有生成器都是同时创建、编译和执行的,但在使用事务时,所有查询都是通过单个连接发送的

因此,所有更新都以串行方式发送到DB服务器,并且这些更新的并发性为0

因此,有5000个knex对象被编译,5000个SQL查询(带有绑定)被发送到DB驱动程序,然后它们被驱动程序缓冲并逐个发送到服务器

但这不应该导致死锁……所以您的代码中可能还有其他一些问题

如果在查询中出现一个错误时,所有数据都不会被还原,那么您可以尝试在多个事务中使用较小的批处理。实际上,我不明白,如果可以重新发送/记录单行数据(如果这些行有问题),为什么需要在事务中进行此类数据更新

我最好的建议是从数据库服务器设置批处理大小、连接池大小和连接限制,以匹配您推送到服务器的工作负载

请随时查看postgreSQL pids CPU使用率98%

如果您通过单个事务执行大量更新,那么很可能导致CPU使用率过高。您应该登录到该SQL server,查看它在该工作负载期间执行的查询类型…可能您是在不同的Transacti中意外地多次并行运行同一更新代码这也可以解释僵局问题

由于单个update语句只能更新一行,因此SQL中的批更新问题非常严重。在单个查询中运行多个更新的一种方法是使用CTE查询


通过这种方式,您可以构建一批更新查询,并将它们作为主查询的预查询添加到数据库中,然后所有这些查询都作为原子操作运行,因此不需要事务来确保整个批处理或什么都没有进入。

听起来好像Node.js或Knex为每一行打开了一个新连接。@a_horse_与_no_name一起运行,那么怎么办您建议解决此问题Bluebird map series以限制并发承诺。您目前基本上同时启动了数千个连接。@Gangstead能否给我一个示例。使用代码above@Gangstead我曾经承诺在1秒后解决每一个更新问题,但这不起作用,好像我要更新10秒的t数小时的数据,这将永远需要,是否有人在没有承诺的情况下做任何事情。因此,它在不到一秒内更新了所有行。我检测到了这个错误死锁,过了一段时间,我发现池可能已满。你能向我解释并发:5意味着什么,它是什么吗?我已经尝试过你的解决方案,它是有效的,但我看到了postgreSQL pids CPU使用率现在一直是98%,特别是这个函数每2分钟循环一次,服务器CPU是10-20%,这是正常的,但是使用这种方法postgreSQL CPU总是很高。每2分钟有一万多个单独的更新,将会有一些高CPU使用率。正如MikaS所说,你将