خانه/ تراکنش‌ها/ ACID

ACID: چهار قولی که یک تراکنش درست باید بدهد

مبتدی ۱۴ دقیقه مطالعه

ACID مخفف چهار ویژگی است که یک پایگاه‌داده‌ی رابطه‌ای باید برای هر تراکنش تضمین کند: Atomicity، Consistency، Isolation و Durability. بیا هرکدام را با مثال درس قبل (انتقال وجه) ببینیم.

Atomicity (اتمی بودن)

یعنی همان چیزی که در درس قبل با COMMIT/ROLLBACK دیدیم: یا همه‌ی دستورهای تراکنش اجرا می‌شوند، یا هیچ‌کدام. اما این‌جا یک نکته‌ی خیلی مهم و غافلگیرکننده وجود دارد که خیلی از برنامه‌نویس‌های تازه‌کار اشتباه متوجه می‌شوند: اگر وسط یک تراکنش، یکی از دستورها با خطا متوقف شود، پایگاه‌داده به‌طور خودکار بقیه‌ی تراکنش را رول‌بک نمی‌کند — این خودِ توست که باید خطا را بگیری و صریحاً ROLLBACK بزنی. بیا این را با چشم خودت ببینی:

امتحانش کن — وقتی یک دستور وسط تراکنش خطا می‌دهد
دقت کن: اجرا دقیقاً همین‌جا با خطا متوقف شد — نه COMMIT اجرا شد و نه ROLLBACK. پایگاه‌داده همچنان داخل یک تراکنش باز (open transaction) مانده که ردیف 'B200' را هم شامل می‌شود. بیا با یک جعبه‌ی دیگر ثابتش کنیم:
امتحانش کن — آیا داده‌ی کامیت‌نشده هم دیده می‌شود؟
دقت کن: با این‌که هنوز هیچ COMMITی نزده‌ایم، ردیف 'B200' در نتیجه‌ی SELECT دیده می‌شود — چون تغییرات ناتمام یک تراکنش، در همان اتصال/Session ایجادکننده‌اش همیشه قابل مشاهده‌اند. حالا جعبه‌ی قبلی را دوباره به‌خاطر بیاور: همان‌جا، درست پس از درج 'B200'، دستور بعدی با خطا متوقف شد — و چون نه COMMIT اجرا شد و نه ROLLBACK، این ردیف دقیقاً به همین شکل در اتصال باز باقی می‌ماند. این دقیقاً همان رفتار پیش‌فرض SQL Server هم هست (مگر این‌که SET XACT_ABORT ON را روشن کنی، یا در کدت با TRY...CATCH صریحاً ROLLBACK بزنی). نتیجه: هرگز فرض نکن atomicity خودش‌به‌خود و بدون مدیریت خطا اتفاق می‌افتد.

راه درست، همیشه گرفتن خطا و رول‌بک صریح است — چیزی شبیه این در SQL Server واقعی:

sql
BEGIN TRY
  BEGIN TRANSACTION;
  INSERT INTO codes VALUES (2, N'B200');
  INSERT INTO codes VALUES (3, N'A100'); -- خطا
  COMMIT;
END TRY
BEGIN CATCH
  ROLLBACK; -- این‌جا صریحاً همه‌چیز را برمی‌گردانیم
END CATCH;

Consistency (پایداری/سازگاری)

یعنی هر تراکنش، پایگاه‌داده را از یک حالت معتبر به یک حالت معتبر دیگر می‌برد — همه‌ی قیدها (PRIMARY KEY، FOREIGN KEY، CHECK، NOT NULL) که در فصل‌های قبل یاد گرفتیم، همیشه رعایت می‌شوند. دقیقاً همان چیزی که در تست بالا دیدیم: قید UNIQUE اجازه نداد کد تکراری درج شود؛ پایگاه‌داده هرگز به حالت نامعتبر (دو کد یکسان) نمی‌رود.

Isolation (ایزوله بودن)

یعنی وقتی چند تراکنش هم‌زمان در حال اجرا هستند، هرکدام باید طوری رفتار کند که انگار تنها تراکنش در حال اجراست — بدون این‌که تغییرات ناتمام یکی، روی دیگری اثر بگذارد. این مفهوم نیازمند چند اتصال هم‌زمان است که در همین Playground تک‌کاربره قابل‌نمایش نیست؛ در دو درس بعدی (قفل‌گذاری و سطوح ایزوله‌سازی) کامل با جزئیات و مثال‌های سناریویی می‌بینیمش.

Durability (پایداری در برابر خرابی)

یعنی به‌محض COMMIT شدن یک تراکنش، تغییراتش حتی در صورت قطع برق یا خرابی سرور، از دست نمی‌رود. یادت هست در درس معماری SQL Server (فصل ۱) گفتیم هر تغییر، پیش از نهایی‌شدن، اول در فایل لاگ تراکنش (.ldf) نوشته می‌شود؟ این همان مکانیزمی است که Durability را تضمین می‌کند.

جمع‌بندی این درس

  • Atomicity: همه یا هیچ — اما فقط اگر خودت خطا را بگیری و ROLLBACK بزنی؛ خودکار نیست.
  • Consistency: تراکنش همیشه قیدهای پایگاه‌داده را رعایت می‌کند.
  • Isolation: تراکنش‌های هم‌زمان نباید روی هم اثر بگذارند (موضوع دو درس بعدی).
  • Durability: بعد از COMMIT، داده حتی با خرابی سیستم هم از بین نمی‌رود — به لطف لاگ تراکنش.

در درس بعدی سراغ قفل‌گذاری می‌رویم: مکانیزمی که SQL Server برای جلوگیری از تداخل تراکنش‌های هم‌زمان استفاده می‌کند.