Node 在连接到 Postgres 方面比 .NET Core 快 20 倍

Posted

技术标签:

【中文标题】Node 在连接到 Postgres 方面比 .NET Core 快 20 倍【英文标题】:Node is 20x faster Than .NET Core in Connecting to Postgres 【发布时间】:2020-04-23 11:22:32 【问题描述】:

我有两台服务器连接到托管在 Azure 上的 PostgresSQL 9.6 db。服务器正在做一件事 - 每 5 秒使用 SELECT 1 查询访问 Postgres 数据库。

连接数据库并获取数据的典型时间:

节点:25 MS .NET Core 3.1 使用 Npsql 4.1.1(我也试过 4.1.2,没有差异):500 MS

我的问题是我的 .NET Core 应用程序在获取数据方面比 Node 慢 20 倍。我相信 .NET Core 出于某种原因没有汇集连接。在本地运行应用程序和在 Azure 应用程序服务上运行应用程序时都会出现这种缓慢 - 没有区别。 我想解决 .NET --> Postgres 缓慢。

请只略读相关细节,不要在此处阅读全部内容 - 我相信只有 .NET Core 代码是相关的。

PsPing 从我的机器到数据库(Node.NET Core 应用程序都在其上运行:

Connecting to foobarPostGres:5432 (warmup): from someIp: 19.98ms
Connecting to foobarPostGres:5432: from someIp: 1.65ms
Connecting to foobarPostGres:5432 from someIp: 1.18ms
Connecting to foobarPostGres:5432: from someIp: 1.23ms
Connecting to foobarPostGres:5432: from someIp: 1.06ms

为了完整起见,NODE 次的示例如下所示(注意它第一次建立连接时也是“慢”):

Attempting to establish a connection...
Elapsed ms:  644.1334999799728
RESP:   '?column?': 1 
Elapsed ms:  22.76109904050827
RESP:   '?column?': 1 
Elapsed ms:  21.984400033950806
RESP:   '?column?': 1 
Elapsed ms:  26.043799996376038
RESP:   '?column?': 1 
Elapsed ms:  22.538798987865448
RESP:   '?column?': 1 

.NET Core 的连接时间如下所示:

5:13:32 PM: SLOW QUERY, CONN TIME: 4153, QUERY TIME: 18 
5:13:53 PM: SLOW QUERY, CONN TIME: 707, QUERY TIME: 17 
5:14:14 PM: SLOW QUERY, CONN TIME: 589, QUERY TIME: 16
5:14:35 PM: SLOW QUERY, CONN TIME: 663, QUERY TIME: 18 
5:14:56 PM: SLOW QUERY, CONN TIME: 705, QUERY TIME: 16 

注意超慢的初始连接时间和很长的后续请求建立连接的时间。

无论如何,因为我很绝望,我现在要转储我所有的代码,并附上解释。连接字符串如下所示:

public static string CONNECTION_STRING 
  get 
    return $"Server=HOST; User Id=USER; Database=DB_NAME; Port=PORT; Password=PWD; SSLMode=Prefer";
  

我的理解是,如果我使用此连接字符串,我应该立即使用连接池。请注意,我已尝试在两个数据库上打开 SSL 并将该行取出 - 它没有帮助。

我的健康检查控制器如下所示:

// GET api/health/getdbhealthselectone
[HttpGet]
[Route("getdbhealthselectone")]
public async Task<IActionResult> GetDbHealthSelectOne()

  int testData = await _healthCheckRepo.RunHealthCheckSelectOne();
  return Ok(testData);

我的健康检查回购方法如下:

 public async Task<int> RunHealthCheckSelectOne()
    

      await using var conn = new NpgsqlConnection(AzureDbConnectionInfo.CONNECTION_STRING);

      var connTimer = System.Diagnostics.Stopwatch.StartNew(); // TODO: Remove this testing line
      await conn.OpenAsync();
      connTimer.Stop(); // TODO: Remove this testing line
      var msToConnect = connTimer.ElapsedMilliseconds; // TODO: Remove this testing line

      int testData = 999;
      var jobsQueryTimer = System.Diagnostics.Stopwatch.StartNew(); // TODO: Remove this testing line0
      await using (var cmd = new NpgsqlCommand("SELECT 1", conn))
      await using (var reader = await cmd.ExecuteReaderAsync())
      while (await reader.ReadAsync()) 
        testData = reader.GetInt32(0);
      ;

      jobsQueryTimer.Stop(); // TODO: Remove this testing line
      var msToQuery = jobsQueryTimer.ElapsedMilliseconds; // TODO: Remove this testing line

      LogQueryIfSlow(msToConnect, msToQuery, _logger); // TODO: Remove this testing line

      return testData;
    

请注意这里的计时器 - await conn.OpenAsync(); 是迄今为止花费大部分时间的原因,查询本身很快。另外,为了节省时间 - 我之前运行过没有async 的代码,没有区别。

最后,如果存在依赖注入问题,存储库位于类库中,API 项目引用它,并且:

services.AddSingleton&lt;IHealthCheckRepository, HealthCheckRepository&gt;();

这是它的看法。

我相信这是所有相关信息 - 我一直在与 Azure 支持通话,他们发现数据库配置没有问题。 .NET Core 应用程序非常轻巧,所以它不会超载并且正在测试中,所以除了我的测试之外没有流量。

额外:为了完整起见,这是我的整个节点应用程序,它访问数据库并发布性能(取出连接数据)。

const  Pool, Client  = require('pg');
const  performance  = require('perf_hooks');

const pool = new Pool(
  user: 'SECRET',
  host: 'SECRET',
  database: 'SECRET',
  password: 'SECRET',
  port: 5432,
)


function runQuery(pool) 
  var t0 = performance.now();
  pool.query('SELECT 1', (err, res) => 
    if (err) 
      console.log('ERROR: ', err.stack)
     else 
      console.log('RESP: ', res.rows[0])
    
    var t1 = performance.now();
    console.log('Elapsed ms: ', t1-t0);
    //pool.end()
);



setInterval(() => runQuery(pool), 5000);

编辑:对于后代,这是修复连接池超时后 .NET Core 中的时间 - 它比节点快,除了初始连接,这似乎需要一段时间,但我没有检查一些默认值:

CONN: 1710 QUERY: 18
CONN: 0 QUERY: 16
CONN: 0 QUERY: 16
CONN: 0 QUERY: 17
CONN: 0 QUERY: 16
CONN: 0 QUERY: 23
CONN: 0 QUERY: 16
CONN: 0 QUERY: 16
CONN: 0 QUERY: 23
CONN: 0 QUERY: 16
CONN: 0 QUERY: 16

【问题讨论】:

1) 节点持有池,在函数中使用池重新测试节点代码或修改 NpgsqlConnection 以具有类似的实现,它们远非类似,我不知道 NpgsqlConnection 是否是任何 gd,使用更多的 ADO 类型作为基线。还要确保结构是一样的。 aka Node 正在建立连接 1 的 ... 方法 RunHealthCheckSelectOne 正在每次调用时创建一个新连接。 尝试设置最小池大小。每 5 秒调用一次并不多,池可能会决定关闭未使用的连接。 @JGH 就是这样:Minimum Pool Size=5。我不明白为什么它会在 300 秒结束之前关闭池中的连接,但确实如此。这使我的连接时间为 0,没有间歇性问题。请写下答案,这样我就可以奖励你了。如果您想在npgsql.org/doc/connection-string-parameters.html 处解释文档的池部分,那会很酷,或者只是回答说“最小池大小”=) 再次感谢您,未成年人,但如果没有您,我将无法解决。 @VSO 只是为了踢球,你能用新的时间更新吗......在你做出改变之后......因为我想看看它现在是否会像我一样转向支持 c#认为会的。 【参考方案1】:

您需要设置最小池大小。这样做可确保无论池使用情况如何,此数量的连接都对数据库保持开放。

默认情况下(至少对于NPGSQL),最小大小为0,所以如果有一段时间不使用连接,它将被关闭。

在您的测试中,您每 5 秒调用一次,这并不多,并且池可能会决定关闭未使用的连接。根据文档,它应该保持打开 300 秒,而不仅仅是 15

【讨论】:

将在可用时返回奖励赏金。 再次阅读后......这就是我的评论导致......每次都会建立一个新连接,因为节点正在重用池连接......所以我的 cmets。但这很好,因为我会写一些“花哨的”膨胀代码来实现同样的目标..所以感谢分享。 @Seabizkit 感谢您的建议 - 您也提供了很多帮助。我觉得这些文档在这个 tbh 上可能会更清楚一些,即使是在回想起来阅读它们之后。【参考方案2】:

有可能第一次查询需要将大量数据从磁盘带入内存,而随后的执行发现所有内容都已在共享缓冲区中。你可以通过运行知道这一点

EXPLAIN (ANALYZE, BUFFERS) <your query>

“读取”和“命中”的数量将告诉您从磁盘读取了多少,以及在 RAM 中命中了多少。

【讨论】:

我添加了ANALYZE 结果。请让我知道这是否告诉您任何可能的解决方案或我应该查看的内容。我真的不明白什么是共享缓冲区——不断分配给缓存的内存? 是否也可以提供EXPLAIN (ANALYZE, BUFFERS)的输出的前28行?张贴的行没有区别。此外,如果您可以检查关键字“实际”后面的值,您可以发现查询需要更多时间的地方。关于shared_buffers,是的,它可以用作缓存。【参考方案3】:

第一个呼叫几乎比其他呼叫长 5 秒。对我来说,这看起来像是一个 IP 地址解析问题。它首先选择一种对给定服务器有缺陷的方法,然后在 5 秒后超时并选择另一种有效的方法。然后它会被缓存一段时间,然后继续正常工作,直到缓存的条目过期。

要查看这是否是问题所在,请将数据库主机的 IP 地址硬编码到您的“hosts”文件中,然后查看是否可以解决问题。如果是这样,那么根本原因将成为您的网络工程师的问题。

在数据库方面,您可以打开慢查询日志记录,log_min_duration_statement 或更好的auto_explain.log_min_duration。但如果我的理论是正确的,这不会显示任何东西。数据库不知道您花了多长时间尝试查找其 IP 地址。

【讨论】:

我假设您的答案是假设 5 秒的数字是具体的。不是,有时需要 2 或 3 秒。或者 6. 5 似乎只是第一次调用的平均值,但没有具体数字。将尝试日志,ty。 当我看到 DNS 问题时,它总是 5.00 秒加上一个正常等待时间的迭代。由于这是您的日志条目显示的内容,因此我怀疑这也是您的问题。但是它可能会在更短的时间内出现,例如,如果两个查询快速连续出现,那么一旦得到另一个解决请求的捎带。如果您的计算机和数据库之间有一个中间服务器(应用程序服务器或连接池),则解决问题可能出现在这些地方,因此您可能需要编辑两个主机文件以确保。 酷,谢谢。我已经按照你的建议打开了日志,神奇的是,通话时间刚好低于我指定的日志时间(1500 毫秒)。一旦我收到记录的查询,就会添加日志。 注意 - 这已经很晚了,但由于我可以从节点上完美快速地访问数据库,我猜这不是 DNS 问题。不过,Ty 再次提出这个想法。

以上是关于Node 在连接到 Postgres 方面比 .NET Core 快 20 倍的主要内容,如果未能解决你的问题,请参考以下文章

在 vagrant 中将 Node.js 连接到 apache 服务器上的 postgres

节点服务器无法连接到 postgres db

使用 sequelize ORM 连接到 node.js 上的 Amazon RDS Postgres 数据库的问题

节点服务器无法通过 Docker 连接到 Postgres,使用 TypeORM

如何从应用程序连接到 ProxySQL

连接到 postgres 数据库失败