如何优化Postgresql max_connections和node-postgres连接池?

简而言之,我无法支持利用Postgresql,Node.js和node-postgres的数据API每分钟超过5000个读取请求。 瓶颈似乎在API和数据库之间。 这里是implmentation的细节。

我为Node.js供电的数据API使用了AWS Postgresql RDS数据库实例(m4.4xlarge – 64 GB mem,16 vCPU,350 GB SSD,没有预configuration的IOPS)。 默认情况下,RDS的max_connections = 5000。 节点API在两个群集之间进行负载均衡,每个群集有4个进程(2个Ec2和4个vCPU,在群集模式下使用PM2运行API)。 我使用node-postgres将API绑定到Postgresql RDS,并试图使用它的连接池function。 以下是我的连接池代码示例:

var pool = new Pool({ user: settings.database.username, password: settings.database.password, host: settings.database.readServer, database: settings.database.database, max: 25, idleTimeoutMillis: 1000 }); /* Example of pool usage */ pool.query('SELECT my_column FROM my_table', function(err, result){ /* Callback code here */ }); 

使用这个实现和testing负载testing仪,我可以在一分钟内支持约5000个请求,平均响应时间约为190ms(这是我所期望的)。 只要我每分钟发出超过5000个请求,在最好的情况下,我的响应时间就会增加到超过1200毫秒,而在最糟糕的情况下,API会经常超时。 监控表明,对于运行Node.js API的EC2,CPU利用率保持在10%以下。 因此,我的重点是数据库和API绑定到数据库。

我试图增加(并减less)node-postgres“max”连接设置,但API响应/超时行为没有改变。 我也尝试在RDS上调配IOPS,但没有任何改进。 另外,有趣的是,我将RDS扩展到了m4.10xlarge(160 GB内存,40个vCPU),而RDS CPU利用率大幅度下降,API的整体性能大幅度下降(甚至无法支持每分钟5000个请求我能够用更小的RDS)。

我在许多方面都处于非同一领域,我不确定如何在每分钟超过5000个请求的情况下最好地确定哪些移动部分是API性能的瓶颈。 如前所述,我已经尝试了基于Postgresqlconfiguration文档和node-postgres文档的各种调整,但无济于事。

如果任何人有如何诊断或优化的意见,我将不胜感激。

UPDATE

扩展到m4.10xlarge后,我执行了一系列的负载testing,改变请求/分钟数和每个池中的最大连接数。 以下是一些监控指标的屏幕截图:

监控指标

数据库连接

为了支持更多的5k请求,同时保持相同的响应速度,您将需要更好的硬件…

简单的math表明: 5000 requests*190ms avg = 950k ms divided into 16 cores ~ 60k ms per core这基本上意味着你的系统是高负载。
(我猜你有一些空闲的CPU,因为在networking上有一些时间丢失了)

现在,您的问题中真正有趣的部分来自于扩展尝试:m4.10xlarge(160 GB mem,40 vCPU)。
CPU利用率的下降表明放大可以释放DB时间资源 – 所以您需要推送更多的请求!
2意见build议:

  • 尝试将连接池增加到max: 70并查看networkingstream量(取决于您可能占用networking的数据量)
  • 另外,您是否需要从应用程序端向数据库同步? 确保你的应用程序可以实际推送更多的请求。

由于您对读取性能感兴趣,可以在两个(或多个)PostgreSQL实例之间build立复制,然后使用pgpool II在实例之间进行负载平衡。

水平缩放意味着如果您下周决定需要10,000个并发读取,您将不会开始在AWS上触及最大实例大小。

你也开始在你的架构中获得一些HA。

很多时候人们会使用pgbouncer作为连接池,即使他们已经在应用程序代码中创build了一个。 pgbouncer运行得非常好,通常更容易configuration和pipe理pgpool,但是不会进行负载平衡。 我不确定在这种情况下是否会对你有所帮助。

根据我的经验,最好的API服务可以做的是根据通话的优先级,为每个API调用使用一个单独的Pool

 var highPriority = new Pool({...max: 20}); // for high-priority API calls var lowPriority = new Pool({...max: 5}); // for low-priority API calls 

然后,您只需为每个API调用使用正确的池,以获得最佳的服务可用性。