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



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

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

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

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

请只浏览相关细节,不要阅读整个事情 - 我相信只有.NET Core代码是相关的。

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

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";
}
}

我的理解是,如果我使用此连接字符串,我应该开箱即用的连接池。请注意,我已经尝试在 db 上转动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<IHealthCheckRepository, HealthCheckRepository>();

这就是它的看法。

我相信这是所有相关信息 - 我一直在与 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

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

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

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

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

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

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

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

EXPLAIN (ANALYZE, BUFFERS) <your query>
">

读取"和"命中"的数量将告诉您从磁盘读取了多少,以及RAM中有多少被击中。

最新更新