مدیریت استخر اتصال (Connection Pooling) و استخر DbContext در NET.: از مفاهیم پایه تا بهینهسازی پیشرفته
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۰۱ ۱۰:۲۰
آدرس: www.dntips.ir
DbContext ارائه گردید که غالباً با استخر اتصال اشتباه گرفته میشود. این مقاله با بررسی دقیق معماری، الگوریتمهای داخلی، پارامترهای پیکربندی، تفاوتهای ساختاری و الگوهای کارآمد (Best Practices)، راهنمایی جامع جهت پیادهسازی و بهینهسازی این دو زیرساخت در اکوسیستم .NET ارائه میدهد.Microsoft.Data.SqlClient یا Npgsql) پیادهسازی شده است. این مکانیزم به جای بستن فیزیکی ارتباط شبکه هنگام فراخوانی متد Close یا Dispose، اتصال را فعال نگهداشته و آن را به یک حافظه میانگیر (Cache) تحت عنوان Pool بازمیگرداند.[ درخواست برنامه ]
│
▼
┌──────────────────────────┐
آیا اتصال آزاد در استخر
موجود است؟
└────────────┬─────────────┘
│
┌───────────┴───────────┐
بله خیر
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
تخصیص اتصال موجود به یا ظرفیت حداکثر اندازه استخر
برنامه تکمیل شده است؟
└──────────────────────────┘ └───────────┬──────────────┘
│
┌───────────┴───────────┐
بله خیر
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
قرارگیری درخواست در صف ایجاد اتصال فیزیکی جدید
(انتظار تا Connection و افزودن به استخر
Timeout) └──────────────────────────┘
└──────────────────────────┘نکته مهم: حتی تغییر در ترتیب کلیدواژههای رشته اتصال باعث ایجاد استخرهای متفاوت میشود:
Server=localhost;Database=AppDb;Database=AppDb;Server=localhost;این دو رشته اتصال منجر به ایجاد دو استخر مجزا میشوند که میتواند باعث هدر رفت منابع سرور گردد.
| پارامتر | مقدار پیشفرض | توضیح و کاربرد |
Pooling | true | فعال یا غیرفعال کردن استخر اتصال. |
Min Pool Size | 0 | حداقل تعداد اتصالاتی که همواره در استخر آماده نگهداشته میشوند. |
Max Pool Size | 100 | حداکثر تعداد اتصالاتی که استخر مجاز به ایجاد آنها است. |
Connection Timeout | 30 | حداکثر زمان انتظار (به ثانیه) برای دریافت اتصال آزاد از استخر قبل از بروز خطا. |
Connection Lifetime | 0 | حداکثر طول عمر یک اتصال (به ثانیه). پس از بازگشت به استخر، اگر عمر آن بیش از این باشد بستهمیشود. |
using یا دستور await using ضروری است.using Microsoft.Data.SqlClient;
public async Task<List<Product>> GetProductsAsync(string connectionString)
{
var products = new List<Product>();
// ساختار await using تضمین میکند که اتصال حتی در صورت بروز خطا به استخر بازمیگردد
await using var connection = new SqlConnection(connectionString);
// در این مرحله، اتصال واقعی از استخر گرفته میشود (یا ایجاد میشود)
await connection.OpenAsync();
await using var command = new SqlCommand("SELECT Id, Title, Price FROM Products", connection);
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
products.Add(new Product
{
Id = reader.GetInt32(0),
Title = reader.GetString(1),
Price = reader.GetDecimal(2)
});
}
return products;
} // در پایان اسکوپ، متد DisposeAsync فراخوانی شده و اتصال به استخر باز میگردد.DbContext در حافظه RAM جهت کاهش هزینههای تخصیص حافظه (Allocation) و جمعآوری زباله (Garbage Collection) است.┌──────────────────────────────────────────────────────────┐
│ EF Core Application │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ DbContext Pool │ │
│ │ [DbContext Instance] [DbContext Instance] │ │
│ └────────────────────────┬─────────────────────────┘ │
└────────────────────────────┼─────────────────────────────┘
│ (بازاستفاده از نمونهها)
▼
┌──────────────────────────────────────────────────────────┐
│ ADO.NET Provider │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Connection Pool │ │
│ │ [Physical Conn 1] [Physical Conn 2] │ │
│ └────────────────────────┬─────────────────────────┘ │
└────────────────────────────┼─────────────────────────────┘
│ (بازاستفاده از اتصالات شبکه)
▼
┌───────────────────┐
│ Database Server │
└───────────────────┘builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));builder.Services.AddDbContextPool<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")),
poolSize: 1024); // تنظیم حداکثر اندازه استخر (پیشفرض 1028 است)DbContextDbContext و آزادسازی حافظه توسط Garbage CollectorDbContext از استخر EF CoreDbContextDbContext به استخر EF Core برای درخواست بعدیSystem.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool.using یا Dispose در کدهای ADO.NET/Dapper.DbContext به هیچ عنوان Thread-Safe نیست. هرگز نباید آن را به صورت Singleton ثبت کرد.DbContext متغیرها یا سرویسهای دارای State ذخیره کنید، این حالت ممکن است بین درخواستهای کاربران مختلف به اشتراک گذاشته شود.// پاکسازی استخر مربوط به یک اتصال خاص SqlConnection.ClearPool(new SqlConnection(connectionString)); // پاکسازی تمامی استخرهای برنامه SqlConnection.ClearAllPools();
sp_reset_connection به صورت اختصاصی اجرا میشود تا تراکنشهای باز، جداول موقت و متغیرهای نشست را پاکسازی کند). توجه به این نکته از نظر کارایی مهم است که اجرای sp_reset_connection هزینه اندکی دارد اما امنیت و ایزولهسازی درخواستها را تضمین مینماید.| ویژگی | Database Connection Pooling | DbContext Pooling |
| لایه مدیریتکننده | ADO.NET Data Provider | Entity Framework Core |
| منبع مورد مدیریت | اتصالات فیزیکی شبکه/داده | DbContext در حافظه |
| وضعیت فعالسازی پیشفرض | فعال | غیرفعال (نیازمند AddDbContextPool) |
| هدف اصلی | کاهش هزینههای Handshake | کاهش allocations و GC Pressure |
| نحوه ثبت / پیکربندی | از طریق Connection String | از طریق AddDbContextPool در DI |
AddDbContextPool تست بار (Load Test) انجام دهید. این بهینهسازی عمدتاً در سامانههایی با نرخ درخواست بالا (High Throughput) تاثیر ملموس نشان میدهد.OpenAsync، ExecuteReaderAsync و SaveChangesAsync استفاده کنید تا نخهای (Threads) برنامه بلوکه نشوند.sys.dm_exec_sessions در SQL Server) زیر نظر بگیرید.