连接和会话不一样吗?
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();
}
我的假设是:
- 当从池中获取连接并重新使用时,我会得到一个异常,因为
tmp_data
已经存在。这不会发生,代码 运行 没问题。
- 我可以通过
CREATE TEMP TABLE IF NOT EXISTS tmp_data ...
解决这个问题。但是我会遇到一个问题,因为 tmp_data
已经存在,所以仍然会有来自上一个插入的行。我可以通过 ON COMMIT DELETE ROWS
. 来解决这个问题
所以我错了吗?池中的连接与会话不是一回事吗?为什么第二次重用连接时临时 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 评论的那样,但是您必须自己注意让会话保持“干净”。
我正在尝试计算何时创建和删除临时 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();
}
我的假设是:
- 当从池中获取连接并重新使用时,我会得到一个异常,因为
tmp_data
已经存在。这不会发生,代码 运行 没问题。 - 我可以通过
CREATE TEMP TABLE IF NOT EXISTS tmp_data ...
解决这个问题。但是我会遇到一个问题,因为tmp_data
已经存在,所以仍然会有来自上一个插入的行。我可以通过ON COMMIT DELETE ROWS
. 来解决这个问题
所以我错了吗?池中的连接与会话不是一回事吗?为什么第二次重用连接时临时 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 评论的那样,但是您必须自己注意让会话保持“干净”。