مقدمه: چرا Lock اهمیت دارد؟
سیستمهای مدیریت پایگاهداده رابطهای برای تضمین درستی دادهها و ایزولیشن تراکنشها از مکانیزم Locking استفاده میکنند. قفلها مانع تداخل مضر عملیات همزمان میشوند و با Isolation Level انتخابی شما تعامل مستقیم دارند. شناخت انواع قفل، شرایط اعمال و سازگاری آنها برای کاهش Deadlock، جلوگیری از Dirty/Non-repeatable/Phantom Reads و بهبود کارایی ضروری است.
مفاهیم پایه و گرانولاریتی قفل
- سطح قفل (Granularity): Row/Key، Page، Table، Database. موتور بر اساس هزینه و حجم داده تصمیم میگیرد.
- Escalation: وقتی تعداد قفلهای ریز زیاد شود، برای کاهش هزینه مدیریت قفلها، قفلها به سطح بالاتر (Page/Table) ارتقا مییابند.
- Latches vs Locks: لچها (Latch) برای همزمانی داخلی حافظه/صفحه هستند و با قفلهای تراکنشی متفاوتند.
انواع قفلها
Shared (S)
برای خواندن. چند تراکنش میتوانند همزمان S روی یک منبع داشته باشند. در حضور S، نوشتن (X) مسدود میشود.
Exclusive (X)
برای نوشتن (INSERT/UPDATE/DELETE). در حضور X هیچ قفل دیگری روی همان منبع سازگار نیست.
Update (U)
مرحلهٔ میانی برای Update: ابتدا U روی منبع گرفته میشود و هنگام تغییر به X ارتقا مییابد. هدف: کاهش احتمال Deadlock در الگوی «خواندن سپس نوشتن».
Intent (IS, IX, SIX)
نشانگرِ قصد قفلگذاری در سطح پایینتر. روی Table/Page گرفته میشوند تا موتور بداند در سطح Row/Key قفل خواهد بود.
IS: قصد Shared — IX: قصد Exclusive — SIX: Shared روی کل + Exclusive روی برخی ردیفها.
Key/Range Locks
روی کلیدهای ایندکس و محدودهها اعمال میشود؛ برای جلوگیری از Phantom در Serializable.
Page / Table Locks
قفل روی کل صفحه (۸KB) یا کل جدول. برای عملیات وسیع یا بر اساس تصمیم بهینهساز.
Schema Locks (Sch-S, Sch-M)
برای عملیات متادیتا/DDL. Sch-S (پایداری اسکیمای خواندنی) در زمان اجرای Query؛ Sch-M (تغییر اسکیمای جدول) هنگام ALTER/DROP و مانند آن.
Bulk Update (BU)
مخصوص Bulk Insert/Load؛ اجازهٔ بارگذاری سریع و جلوگیری از تداخل خواندن ناهمساز.
جدول سازگاری قفلها (Compatibility Matrix)
ماتریس زیر رایجترین قفلها را در سطح یک منبع واحد (Row/Page/Table) نشان میدهد. ✅ یعنی سازگار و ❌ یعنی ناسازگار.
Requested \ Held | S | U | X | IS | IX | SIX |
---|---|---|---|---|---|---|
S | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
U | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
X | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
IS | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
IX | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
SIX | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
نکته: سازگاری بالا برای «یک منبع یکسان» بیان شده است. در عمل، Intent Lockها روی سطح بالاتر (Table/Page) گرفته میشوند تا قفلهای ریز (Row/Key) در سطح پایین مدیریتپذیر شوند.
شرایط اعمال قفل و نقش Isolation Level
- نوع عملیات: SELECT → S؛ INSERT/UPDATE/DELETE → X (اغلب با Intent در سطح بالاتر).
- Isolation Level: Read Uncommitted قفلگذاری حداقلی (ممکن است Dirty Read)، Read Committed پیشفرض (قفلگذاری خواندن/نوشتن)، Repeatable Read حفظ S روی رکوردهای خواندهشده، Serializable استفاده از Range/Key Locks برای جلوگیری از Phantom، Snapshot/RCSI استفاده از Row Versioning (کاهش بنبستها و قفلهای خواندن).
- تصمیم بهینهساز: انتخاب Row/Page/Table Lock و Escalation بر اساس هزینه/تعداد ردیفها.
Deadlock، Update Lock و توصیههای پیشگیری
Deadlock زمانی رخ میدهد که دو تراکنش منابع یکدیگر را منتظر بگذارند. استفاده از U Lock در الگوی «خواندن سپس نوشتن» باعث میشود بهجای چندین S همزمان (که همگی درخواست ارتقای X میدهند)، از ابتدا مسیر ارتقا مدیریتپذیر باشد.
- دسترسی به منابع را به یک ترتیب ثابت استاندارد کنید.
- تراکنشها را کوتاه نگه دارید؛ از انتظارهای غیرضروری داخل تراکنش پرهیز کنید.
- ایندکسگذاری مناسب برای کاهش اسکنها و دامنهٔ قفلها.
- در سناریوهای خواندن زیاد، Snapshot/RCSI را بررسی کنید.
Lock Hints (نمونههای پرکاربرد) و مثالها
برخی Hintهای رایج
WITH (HOLDLOCK)
≈ Serializable روی محدودهٔ هدفWITH (UPDLOCK)
گرفتن U بهجای S برای کاهش بنبستWITH (ROWLOCK|PAGLOCK|TABLOCK)
پیشنهاد سطح قفلWITH (NOLOCK)
≈ Read Uncommitted (خطر Dirty Read)WITH (XLOCK|S|U)
اجبار نوع قفل
فعالسازی Row Versioning
-- Read Committed Snapshot Isolation (RCSI)
ALTER DATABASE YourDb SET READ_COMMITTED_SNAPSHOT ON;
-- Snapshot Isolation (نیاز به SET ALLOW_SNAPSHOT_ISOLATION ON)
ALTER DATABASE YourDb SET ALLOW_SNAPSHOT_ISOLATION ON;
-- استفاده:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN;
SELECT ... FROM dbo.T WHERE ...;
COMMIT;
توجه: RCSI بهصورت شفاف روی Read Committed اثر میگذارد؛ Snapshot نیازمند SET سطح تراکنشی است.
نمونه سناریوی بهبود بنبست با UPDLOCK
-- جدول نمونه
CREATE TABLE dbo.Inventory(
Id int IDENTITY PRIMARY KEY,
Sku varchar(50) NOT NULL,
Qty int NOT NULL
);
INSERT INTO dbo.Inventory(Sku, Qty) VALUES ('A-100', 5), ('B-200', 3);
-- تراکنش 1
BEGIN TRAN;
-- خواندن با UPDLOCK به جای S
SELECT Qty FROM dbo.Inventory WITH (UPDLOCK, ROWLOCK)
WHERE Sku = 'A-100';
-- منطق برنامه...
UPDATE dbo.Inventory SET Qty = Qty - 1 WHERE Sku = 'A-100';
COMMIT;
-- در تراکنشهای همروند مشابه، UPDLOCK احتمال بنبست را کم میکند.
مشاهده و مانیتورینگ قفلها
DMVها
-- قفلهای جاری
SELECT
tl.request_session_id AS spid,
tl.resource_type,
tl.resource_database_id,
tl.resource_associated_entity_id AS hobt_id,
tl.request_mode,
tl.request_status
FROM sys.dm_tran_locks AS tl
ORDER BY tl.request_session_id;
-- درخواستهای درحال اجرا
SELECT
r.session_id, r.status, r.command, r.wait_type, r.wait_time,
r.blocking_session_id, r.cpu_time, r.logical_reads, r.row_count, r.sql_handle
FROM sys.dm_exec_requests AS r
WHERE r.session_id > 50;
-- تراکنشهای باز
DBCC OPENTRAN;
برای نگاشت hobt_id
به جدول/ایندکس از sys.partitions
و sys.indexes
کمک بگیرید.
نمونهٔ اتصال قفل به آبجکت
SELECT
tl.request_session_id,
tl.resource_type,
tl.request_mode,
OBJECT_NAME(p.object_id) AS object_name,
i.name AS index_name
FROM sys.dm_tran_locks AS tl
JOIN sys.partitions AS p
ON p.hobt_id = tl.resource_associated_entity_id
LEFT JOIN sys.indexes AS i
ON i.object_id = p.object_id AND i.index_id = p.index_id
WHERE tl.resource_type IN ('OBJECT','PAGE','KEY','RID')
ORDER BY tl.request_session_id;
Serializable و Range/Key Locks (جلوگیری از Phantom)
-- جدول نمونه
CREATE TABLE dbo.Orders(
Id int IDENTITY PRIMARY KEY,
CustomerId int NOT NULL,
Amount money NOT NULL
);
INSERT INTO dbo.Orders(CustomerId, Amount) VALUES (1, 120), (1, 80), (2, 200);
-- تراکنش A: جلوگیری از رکوردهای شبح (Phantom)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRAN;
-- این کوئری Range/Key Locks میگیرد
SELECT SUM(Amount) FROM dbo.Orders WHERE CustomerId = 1;
-- تا پایان تراکنش، درج/حذف رکوردهای جدید با CustomerId=1 مسدود میشود.
-- ... منطق برنامه ...
COMMIT;
در این حالت درج رکورد جدید با CustomerId = 1
توسط تراکنش دیگر، تا پایان تراکنش A بلوکه میشود.
نکات و الگوهای عملی برای کنترل Lock
- ایندکسهای هدفمند بسازید تا محدودهٔ قفل کوچکتر شود (Row/Key به جای Page/Table).
- ترتیب دسترسی به جداول/رکوردها را استاندارد کنید تا Deadlock حلقهای شکل نگیرد.
- تراکنشها را کوتاه نگه دارید؛ I/O خارجی (Network/Prompt/UI) را بیرون از تراکنش انجام دهید.
- برای بارخوانی سنگین، RCSI/Snapshot را بررسی کنید تا قفلهای خواندن حذف/کاهش یابد.
- Escalation را زیر نظر بگیرید؛ در صورت نیاز با
ALTER TABLE ... SET (LOCK_ESCALATION = DISABLE|AUTO|TABLE)
تنظیم کنید. - از Hintها با احتیاط استفاده کنید؛ NOLOCK میتواند نتایج نادرست بدهد.
جمعبندی
قفلها ستون فقرات کنترل همزمانی در RDBMS هستند. با شناخت انواع قفل (S/U/X/IS/IX/SIX)، ماتریس سازگاری، گرانولاریتی و نقش Isolation Level میتوانید تجربهای پایدار، درست و کارا برای تراکنشها رقم بزنید. در سناریوهای «خواندن زیاد»، نسخهگذاری ردیف (Snapshot/RCSI) و در سناریوهای «نوشتن رقابتی»، طراحی تراکنش کوتاه، ایندکس خوب و الگوی UPDLOCK معمولاً بهترین نتایج را میدهند.
خلاصه مقاله
این مقاله با مرور انواع قفلهای دیتابیس (S، U، X، Intentها، Key/Range، Page/Table و Schema) و نشاندادن ماتریس سازگاری، توضیح داد که قفلها چگونه بر اساس نوع عملیات، Isolation Level و تصمیم بهینهساز اعمال میشوند و چطور میتوان با الگوهایی مانند UPDLOCK، ایندکسگذاری هدفمند، تراکنش کوتاه و Snapshot/RCSI، از بنبست و ناهماهنگی داده جلوگیری کرد. همچنین با DMVها و نمونهکدها، روش مشاهده و تحلیل قفلها ارائه شد تا بتوانید در پروژههای واقعی تعادل بین صحت و کارایی را برقرار کنید.