ایزولاسیون تراکنشها در ASP.NET Core
مقدمه
مدیریت تراکنشها و کنترل سطح ایزولاسیون (Isolation Level) یکی از مهمترین مباحث در توسعه برنامههای مبتنی بر پایگاه داده با ASP.NET Core است. انتخاب صحیح سطح ایزولاسیون میتواند تعادل مناسبی بین یکپارچگی دادهها و عملکرد سیستم ایجاد کند. در این مقاله به بررسی مفهوم ایزولاسیون، سطوح مختلف آن، روشهای پیادهسازی در ASP.NET Core (با استفاده از EF Core و ADO.NET) و نکات کاربردی میپردازیم.
مفهوم تراکنش و Isolation
تراکنش (Transaction) یک واحد منطقی از کار است که شامل یک یا چند عملیات روی پایگاه داده میشود و باید به صورت اتمیک (Atomic) اجرا شود؛ یعنی یا همه عملیات با موفقیت انجام شوند یا هیچکدام. ویژگیهای ACID (Atomicity, Consistency, Isolation, Durability) چهار اصل بنیادین تراکنشها هستند. ایزولاسیون (Isolation) مشخص میکند که تغییرات ایجاد شده توسط یک تراکنش، چگونه و از چه زمانی برای سایر تراکنشهای همزمان قابل مشاهده است.
درک صحیح سطوح ایزولاسیون به توسعهدهنده کمک میکند تا از بروز مشکلات رایج همزمانی مانند Dirty Read، Non-Repeatable Read و Phantom Read جلوگیری کند.
سطوح ایزولاسیون (Isolation Levels)
در SQL Server و اکثر پایگاههای داده رابطهای، پنج سطح استاندارد ایزولاسیون وجود دارد:
| سطح ایزولاسیون | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| Read Uncommitted | ممکن است | ممکن است | ممکن است |
| Read Committed | جلوگیری میشود | ممکن است | ممکن است |
| Repeatable Read | جلوگیری میشود | جلوگیری میشود | ممکن است |
| Serializable | جلوگیری میشود | جلوگیری میشود | جلوگیری میشود |
| Snapshot | جلوگیری میشود | جلوگیری میشود | جلوگیری میشود |
توضیح مشکلات همزمانی
- Dirty Read: خواندن دادهای که توسط تراکنش دیگری تغییر کرده اما هنوز commit نشده است.
- Non-Repeatable Read: اگر یک تراکنش یک ردیف را دو بار بخواند و بین این دو خواندن، تراکنش دیگری آن را تغییر داده و commit کرده باشد، مقادیر متفاوتی مشاهده میشود.
- Phantom Read: وقتی یک تراکنش مجموعهای از ردیفها را با یک شرط خاص میخواند و تراکنش دیگری ردیفهایی را اضافه یا حذف میکند که شرط را برآورده میکنند، در خواندن مجدد، ردیفهای جدیدی ظاهر میشوند.
روشهای پیادهسازی تراکنش در ASP.NET Core
استفاده از EF Core
Entity Framework Core (EF Core) به عنوان ORM محبوب در ASP.NET Core، امکانات قدرتمندی برای کار با تراکنشها ارائه میدهد. برای شروع یک تراکنش با سطح ایزولاسیون مشخص، از متد BeginTransaction روی DbContext.Database استفاده میکنیم.
using (var context = new ApplicationDbContext())
{
using (var transaction = await context.Database.BeginTransactionAsync(IsolationLevel.Serializable))
{
try
{
// عملیات اول
var product = await context.Products.FindAsync(1);
product.Stock -= 10;
await context.SaveChangesAsync();
// عملیات دوم
context.Orders.Add(new Order { ProductId = 1, Quantity = 10 });
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
}
}
نکات مهم در EF Core
- سطح پیشفرض ایزولاسیون در SQL Server معمولاً
ReadCommittedاست. - برای استفاده از
IsolationLevel.Snapshotباید ابتدا گزینهALLOW_SNAPSHOT_ISOLATIONرا در پایگاه داده فعال کنید. - در EF Core 6 به بعد میتوانید از تراکنشهای ضمنی با
ExecutionStrategyبرای مدیریت خطاهای گذرا استفاده کنید.
استفاده از ADO.NET
اگر به کنترل بیشتری روی تراکنش نیاز دارید یا از EF Core استفاده نمیکنید، میتوانید از کلاسهای ADO.NET مانند SqlConnection و SqlTransaction استفاده کنید.
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
using (var transaction = connection.BeginTransaction(IsolationLevel.RepeatableRead))
{
try
{
var command1 = new SqlCommand("UPDATE Products SET Stock = Stock - 10 WHERE Id = 1", connection, transaction);
await command1.ExecuteNonQueryAsync();
var command2 = new SqlCommand("INSERT INTO Orders (ProductId, Quantity) VALUES (1, 10)", connection, transaction);
await command2.ExecuteNonQueryAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
مقایسه سطوح ایزولاسیون
انتخاب سطح مناسب به نیازمندیهای برنامه بستگی دارد:
- Read Uncommitted: بالاترین کارایی، اما ریسک بالای خواندن دادههای ناپایدار. فقط در سناریوهایی مانند گزارشگیری که دقت کماهمیت است، استفاده شود.
- Read Committed: پیشفرض اکثر سیستمها. از Dirty Read جلوگیری میکند و برای اکثر کاربردهای عمومی مناسب است.
- Repeatable Read: برای مواقعی که نیاز است یک تراکنش دادههای یکسانی را در طول اجرای خود ببیند. قفلهای بیشتری نگه میدارد و میتواند باعث کاهش همزمانی شود.
- Serializable: قویترین سطح ایزولاسیون که تمام مشکلات همزمانی را برطرف میکند، اما هزینه بالایی از نظر قفلگذاری و احتمال بنبست دارد.
- Snapshot: از نسخههای قبلی ردیفها در
tempdbاستفاده میکند و بدون قفلگذاری طولانی، ایزولاسیون قوی فراهم میکند. مناسب برای برنامههای با خواندن زیاد و نوشتن کم.
نکات مهم و سناریوهای استفاده
- سیستمهای بانکی و مالی: معمولاً از
SerializableیاSnapshotبرای جلوگیری از هرگونه ناسازگاری استفاده میکنند. - فروشگاههای آنلاین با ترافیک بالا: بهتر است از
ReadCommittedیاSnapshotاستفاده شود تا همزمانی حفظ شود. - گزارشگیری تحلیلی: گاهی
ReadUncommittedبا احتیاط قابل قبول است، اماSnapshotگزینه امنتری است. - مدیریت بنبست (Deadlock): سطوح ایزولاسیون بالاتر احتمال بنبست را افزایش میدهند. همیشه سعی کنید تراکنشها را کوتاه نگه دارید و ترتیب دسترسی به منابع را یکسان کنید.
- آزمایش و پایش: قبل از استقرار نهایی، سطح ایزولاسیون را تحت بار واقعی آزمایش کنید و با ابزارهایی مانند SQL Profiler یا Extended Events رفتار قفلها را بررسی کنید.
جمعبندی
ایزولاسیون تراکنشها یکی از ارکان اصلی یکپارچگی دادهها در برنامههای ASP.NET Core است. با شناخت دقیق سطوح مختلف و انتخاب آگاهانه آنها بر اساس نیازمندیهای کسبوکار، میتوانید هم از صحت دادهها اطمینان حاصل کنید و هم عملکرد سیستم را در سطح مطلوب نگه دارید. استفاده از EF Core یا ADO.NET هر دو امکان کنترل کامل سطح ایزولاسیون را فراهم میکنند، اما توصیه میشود در پروژههای واقعی ابتدا با سطح پیشفرض شروع کرده و در صورت نیاز به تدریج به سطوح قویتر حرکت کنید.