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

Blocking: وقتی یک تراکنش پشت تراکنش دیگر منتظر می‌ماند

متوسط ۱۱ دقیقه مطالعه

در درس قبل دیدیم وقتی Session A روی یک ردیف قفل انحصاری دارد، Session B باید منتظر بماند. این وضعیت انتظار، اسم دارد: Blocking. خودِ Blocking یک باگ نیست — بخش طبیعی و لازم هر پایگاه‌داده‌ی چندکاربره است. مشکل از جایی شروع می‌شود که این انتظار، خیلی طول بکشد.

سناریوی کامل Blocking

Session A
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
-- کار دیگری هم انجام می‌دهد (مثلاً ارتباط با یک API خارجی)...
COMMIT; ← بالاخره این‌جا اجرا می‌شود
Session B
UPDATE accounts SET balance = balance + 50000 WHERE id = 1;
-- Blocked: منتظر رهاشدن قفل ردیف id=1
-- همچنان منتظر...
-- تازه الان، بعد از COMMIT شدن Session A، اجرا می‌شود

اگر Session A به هر دلیلی — کدنویسی ضعیف، فراخوانی یک سرویس خارجی کند وسط تراکنش، یا فراموش‌کردن COMMIT — دیر تمام شود، Session B (و هر Session دیگری که منتظر همان ردیف است) هم به همان اندازه معطل می‌ماند. این دقیقاً همان چیزی است که کاربران به‌عنوان «سایت/اپلیکیشن قفل کرده» تجربه می‌کنند.

Blocking در برابر Deadlock — یک تفاوت مهم

Blocking یعنی یک تراکنش منتظر دیگری می‌ماند — با گذشت زمان، معمولاً خودش حل می‌شود (وقتی تراکنش اول COMMIT/ROLLBACK کند). اما Deadlock (بن‌بست) حالت خاص و خطرناک‌تری است: دو تراکنش، هرکدام منتظر منبعی هستند که در قفل آن‌یکی است — یعنی هیچ‌کدام هرگز خودش‌به‌خود آزاد نمی‌شود. SQL Server این حالت را تشخیص می‌دهد و یکی از دو تراکنش را به‌عنوان «قربانی» انتخاب و با خطا لغو می‌کند تا دیگری بتواند ادامه دهد.

چطور Blocking را کاهش دهیم

  • تراکنش‌ها را کوتاه نگه دار: هیچ‌وقت وسط یک تراکنش، منتظر یک فراخوانی شبکه‌ای یا کاربر نمان.
  • ایندکس مناسب داشته باش: پرس‌وجوی بدون ایندکس مناسب، ممکن است مجبور شود ردیف‌های بیشتری را قفل کند (موضوع فصل ۱۰).
  • سطح ایزوله‌سازی مناسب انتخاب کن: بعضی سطوح ایزوله‌سازی، قفل کمتری می‌گیرند — موضوع دقیق درس بعدی.
  • ابزار تشخیص: در SQL Server واقعی، sp_who2 یا Activity Monitor نشان می‌دهند کدام Session، کدام Session دیگر را مسدود کرده.

پیدا کردن Session مسدودکننده

در SQL Server واقعی، این پرس‌وجو نشان می‌دهد چه کسی منتظر چه کسی است:

sql
SELECT r.session_id,
       r.blocking_session_id,         -- کسی که قفل را نگه داشته
       r.wait_type, r.wait_time,
       DB_NAME(r.database_id) AS DatabaseName,
       t.text AS RunningSql
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.blocking_session_id <> 0;

ستون blocking_session_id شماره‌ی Session مقصر را می‌دهد. با دستور زیر قدیمی‌ترین تراکنش باز پایگاه‌داده‌ی فعلی را هم می‌بینی:

sql
DBCC OPENTRAN;

چه کار کنیم؟

گاماقدام
۱ابتدا بفهم تراکنش مسدودکننده چه می‌کند (RunningSql و کاربر/برنامه).
۲اگر کار مشروع است و به‌زودی تمام می‌شود، صبر کن.
۳اگر تراکنش رها شده (مثلاً برنامه فراموش کرده COMMIT کند)، از صاحب برنامه بخواه آن را ببندد.
۴در آخرین راه و با هماهنگی، KILL 53; (شماره‌ی Session) تراکنش را Rollback می‌کند.
KILL تراکنش را Rollback می‌کند و ممکن است Rollback خودش زمان‌بر باشد. آن را فقط وقتی استفاده کن که از اثرش مطمئنی.

اگر کندی کلی سرور را بررسی می‌کنی، مقاله‌ی چرا SQL Server کند شده است؟ بررسی Blocking را در کنار CPU، حافظه و دیسک توضیح داده است.

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

  • Blocking یعنی یک تراکنش منتظر رهاشدن قفل تراکنش دیگر بماند — پدیده‌ای طبیعی، نه یک باگ.
  • مشکل واقعی وقتی شروع می‌شود که تراکنش نگه‌دارنده‌ی قفل، بیش‌ازحد طول بکشد.
  • Deadlock حالت خاص‌تری است: دو تراکنش منتظر یکدیگرند و SQL Server باید یکی را قربانی کند.
  • کوتاه نگه‌داشتن تراکنش‌ها، مؤثرترین راه کاهش Blocking است.

در آخرین درس این فصل، سراغ سطوح ایزوله‌سازی می‌رویم: چطور می‌توان با تنظیم دقیق‌تر، بین سرعت و امنیت داده در برابر تداخل هم‌زمان، تعادل برقرار کرد.

تمرین‌های خودآزمایی

اول خودت جواب بده، بعد پاسخ را باز کن و مقایسه کن.

۱Blocking و Deadlock چه فرقی دارند؟نمایش پاسخ
پاسخ

Blocking انتظار موقت یک تراکنش برای دیگری است؛ Deadlock انتظار چرخه‌ای است که SQL Server با قربانی‌کردن یکی از تراکنش‌ها آن را می‌شکند.

۲چطور تراکنش مسدودکننده را پیدا کنیم؟نمایش پاسخ
پاسخ

ستون blocking_session_id در sys.dm_exec_requests یا دستور sp_who2.