连接和会话不一样吗?

Isn't a connection the same as a session?

我正在尝试计算何时创建和删除临时 table。

我认为临时 table 在会话结束时被删除,但这个测试让我不确定。

连接字符串有 Pooling=true;Maximum Pool Size=50 所以有池,最多 50 个连接。

我运行下面这段代码并发,一次30个作业。我可以看到使用了 30 个不同的进程 ID 值(这让我假设总共打开并重用了 30 个连接),无论我 运行 代码多少次。所以合并似乎有效。

using (var connection = new NpgsqlConnection(connectionString))
{
    var insertIds = GetInsertIds(1000).ToList();

    await connection.OpenAsync();

    var transaction = connection.BeginTransaction();

    await connection.ExecuteAsync(@"
        CREATE TEMP TABLE tmp_data
        (
            insertid INTEGER NOT NULL
        );", transaction: transaction);

    const string copyItemSql = @"
        COPY tmp_data(
            insertid
        ) FROM STDIN (FORMAT BINARY)
    ";

    using (var writer = connection.BeginBinaryImport(copyItemSql))
    {
        foreach (var insertId in insertIds)
        {
            writer.StartRow();
            writer.Write(insertId, NpgsqlDbType.Integer);
        }
    }

    await connection.ExecuteAsync(@"
        INSERT INTO data (insertid)
            SELECT tmpd.insertid
            FROM tmp_data tmpd;
    ", transaction: transaction);

    transaction.Commit();
}

我的假设是:

  1. 当从池中获取连接并重新使用时,我会得到一个异常,因为 tmp_data 已经存在。这不会发生,代码 运行 没问题。
  2. 我可以通过 CREATE TEMP TABLE IF NOT EXISTS tmp_data ... 解决这个问题。但是我会遇到一个问题,因为 tmp_data 已经存在,所以仍然会有来自上一个插入的行。我可以通过 ON COMMIT DELETE ROWS.
  3. 来解决这个问题

所以我错了吗?池中的连接与会话不是一回事吗?为什么第二次重用连接时临时 table 不存在?

我查看了 Npgsql 的实现,这是当连接返回池时运行的:

void GenerateResetMessage()
{
    var sb = new StringBuilder("SET SESSION AUTHORIZATION DEFAULT;RESET ALL;");
    var responseMessages = 2;
    if (DatabaseInfo.SupportsCloseAll)
    {
        sb.Append("CLOSE ALL;");
        responseMessages++;
    }
    if (DatabaseInfo.SupportsUnlisten)
    {
        sb.Append("UNLISTEN *;");
        responseMessages++;
    }
    if (DatabaseInfo.SupportsAdvisoryLocks)
    {
        sb.Append("SELECT pg_advisory_unlock_all();");
        responseMessages += 2;
    }
    if (DatabaseInfo.SupportsDiscardSequences)
    {
        sb.Append("DISCARD SEQUENCES;");
        responseMessages++;
    }
    if (DatabaseInfo.SupportsDiscardTemp)
    {
        sb.Append("DISCARD TEMP");
        responseMessages++;
    }

    responseMessages++;  // One ReadyForQuery at the end

    _resetWithoutDeallocateMessage = PregeneratedMessage.Generate(WriteBuffer, QueryMessage, sb.ToString(), responseMessages);
}

NpgsqlDatabaseInfo.SupportsDiscardTemp 是这样设置的:

public virtual bool SupportsDiscardTemp => Version >= new Version(8, 3, 0);

因此,只要您使用至少 8.3 的 PostgreSQL 版本,就会出现这种行为。

您可以使用连接字符串参数 No Reset On Close=true 避免此重置,正如下面 Shay Rojansky 评论的那样,但是您必须自己注意让会话保持“干净”。