ADO.NET и Npgsql: соединения, команды, пул соединений

↑ 4.1 ADO.NET, Npgsql и Dapper · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Вопросы про пул соединений и утечки соединений — классика. Они показывают, понимаете ли вы, что находится под ORM.

Объяснение

ADO.NET — базовый слой доступа к данным в .NET. Провайдер (Npgsql для PostgreSQL, Microsoft.Data.SqlClient для SQL Server) реализует общие абстракции.

АбстракцияРоль
DbConnectionсоединение с БД
DbCommandSQL-команда с параметрами
DbDataReaderпотоковое чтение строк вперёд
DbTransactionтранзакция
DbDataSource (Npgsql NpgsqlDataSource)фабрика соединений с пулом
var dataSource = NpgsqlDataSource.Create(connectionString);   // singleton на приложение
builder.Services.AddNpgsqlDataSource(connectionString);
 
await using var conn = await dataSource.OpenConnectionAsync(ct);
await using var cmd = new NpgsqlCommand("select id, name from users where id = $1", conn)
{
    Parameters = { new() { Value = id } }
};
await using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
    users.Add(new User(reader.GetGuid(0), reader.GetString(1)));

Пул соединений

Открытие физического соединения дорого (TCP, TLS, аутентификация). Open() берёт соединение из пула, Dispose() (Close()) возвращает его обратно — физически оно не закрывается. Размер пула настраивается в строке подключения: Maximum Pool Size=100.

Нюансы и подводные камни

  • Не закрыл соединение — оно не вернулось в пул: «timeout waiting for a connection from the pool». Всегда using/await using.
  • Держите соединение как можно меньше: открыл — выполнил — закрыл.
  • Максимальный размер пула × число инстансов не должен превышать max_connections сервера PostgreSQL (используйте PgBouncer).
  • Read и ExecuteReader держат соединение, пока reader открыт: не вызывайте второй запрос на том же соединении без MARS/multiplexing.
  • Для больших выборок используйте CommandBehavior.SequentialAccess и потоковое чтение.

Практика

  1. Воспроизведите исчерпание пула, «забыв» закрыть соединение.
  2. Настройте NpgsqlDataSource в DI и прочитайте данные через reader.
  3. Сравните время запроса с пулом и Pooling=false.

Вопросы с ответами

Связанные темы