
مقدمه : چرا باید DBMS_LOCK را یاد بگیریم؟
وقتی صحبت از قفلها (Locks) در پایگاهداده اوراکل میشه، اغلب ذهنمون میره سمت قفلهای ردیفی (Row-level Locks) یا جدولی (Table-level Locks).
اما گاهی ما نیاز داریم خودمون قفلهایی رو تعریف کنیم که تحت کنترل برنامهنویس باشه، نه خود پایگاهداده.
اینجاست که پکیج قدرتمند و در عین حال نسبتاً ناشناختهی DBMS_LOCK وارد میشه!
با استفاده از این پکیج میتونیم:
- فقط یک فرآیند رو در آنِ واحد اجرا کنیم.
- از اجراهای همزمان ناخواسته جلوگیری کنیم.
- بین Sessionهای مختلف هماهنگی ایجاد کنیم.
- قفلهایی با منطق دلخواه خودمون تعریف کنیم.
در این مقاله آموزش اوراکل، به زبان ساده و همراه با مثالهای عملی، همه چیز درباره DBMS_LOCK را به شما یاد میدهم 💡
اگر می خواهید در مورد پکیج DBMS_METADATA در بخش آموزش PL/SQL بیشتر آشنا بشید نوشته زیر را مطالعه کنید:
در این نوشته شما می خوانید
DBMS_LOCK چیست؟
DBMS_LOCK یک بسته سیستمی (built-in package) در Oracle هست که امکان ایجاد قفلهای منطقی توسط کاربر رو فراهم میکنه.
این قفلها از قفلهای استاندارد اوراکل (مثل Row Lock) مستقل هستند و برای مدیریت منطقی پردازشها بسیار کاربردیاند.
مهمترین توابع و رویههای DBMS_LOCK
۱. ALLOCATE_UNIQUE: ساخت قفل اختصاصی
DBMS_LOCK.ALLOCATE_UNIQUE(
lockname IN VARCHAR2,
lockhandle OUT VARCHAR2,
expiration_secs IN INTEGER DEFAULT 86400);
با این تابع، میتونیم یک قفل با نام دلخواه بسازیم که یک handle یکتا براش تولید میشه.
این handle رو در ادامه برای گرفتن یا آزاد کردن قفل استفاده میکنیم.
مثال:
DECLARE
l_handle VARCHAR2(128);
BEGIN
DBMS_LOCK.ALLOCATE_UNIQUE('my_custom_lock', l_handle);
DBMS_OUTPUT.PUT_LINE('LOCK HANDLE: ' || l_handle);
END;
۲. REQUEST: گرفتن قفل
DBMS_LOCK.REQUEST(
id IN NUMBER,
lockmode IN INTEGER DEFAULT 6,
timeout IN INTEGER DEFAULT MAXWAIT,
release_on_commit IN BOOLEAN DEFAULT FALSE)
RETURN INTEGER;
با این تابع، میتونیم سعی کنیم قفل رو بگیریم. اگر قفل در دست session دیگهای باشه،
طبق timeout مشخصشده منتظر میمونیم.
- lockmode=6 برای قفل Exclusive استفاده میشه.
- اگر timeout=0 باشه، بلافاصله اگر قفل در دسترس نباشه، خطا برمیگردونه.
مثال:
DECLARE
l_handle VARCHAR2(128);
l_result INTEGER;
BEGIN
DBMS_LOCK.ALLOCATE_UNIQUE('my_process_lock', l_handle);
l_result := DBMS_LOCK.REQUEST(
id => DBMS_LOCK.NAME_LOCK(l_handle),
lockmode => ۶,
timeout => ۵,
release_on_commit => FALSE);
IF l_result = 0 THEN
DBMS_OUTPUT.PUT_LINE('Lock acquired');
ELSE
DBMS_OUTPUT.PUT_LINE('Failed to acquire lock');
END IF;
END;
۳. RELEASE: آزاد کردن قفل
DBMS_LOCK.RELEASE(id IN NUMBER)
RETURN INTEGER;
با این تابع، قفلی که در دست session فعلی هست آزاد میشه.
مثال:
l_result := DBMS_LOCK.RELEASE(DBMS_LOCK.NAME_LOCK(l_handle));
۴. CONVERT: تغییر نوع قفل
اگر بخوای قفل رو از نوع مثلاً Share به Exclusive تغییر بدی، از CONVERT استفاده میکنی.
یک مثال عملی کامل: جلوگیری از اجرای همزمان یک فرآیند
فرض کن فقط میخوای یک فرآیند آپدیت روی جدول خاصی در یک زمان اجرا بشه. در این صورت:
DECLARE
l_handle VARCHAR2(128);
l_result INTEGER;
BEGIN
-- Gereftan Lock Shakhsi baraye in Farayand
DBMS_LOCK.ALLOCATE_UNIQUE('update_table_process', l_handle);
-- Talash baraye gereftan lock
l_result := DBMS_LOCK.REQUEST(DBMS_LOCK.NAME_LOCK(l_handle),
۶, ۱۰, FALSE);
IF l_result = 0 THEN
DBMS_OUTPUT.PUT_LINE('Lock acquired! Proceeding with update...');
-- Update Code
UPDATE some_table SET column_name = 'value' WHERE condition;
COMMIT;
-- Azad sazii Lock
l_result := DBMS_LOCK.RELEASE(DBMS_LOCK.NAME_LOCK(l_handle));
DBMS_OUTPUT.PUT_LINE('Lock released!');
ELSE
DBMS_OUTPUT.PUT_LINE('Could not acquire lock. Try again later.');
END IF;
END;
نکات مهم حرفهای برای DBMS_LOCK
- قفلها فقط در همان Session معتبرند. پس در Session جدید دوباره باید گرفته شوند.
- زمان پیشفرض قفلها ۱ روز (۸۶۴۰۰ ثانیه) است، مگر اینکه با
expiration_secsمقدار دیگری بدی. - با COMMIT، اگر
release_on_commit=TRUEباشه، قفل آزاد میشه. پس با دقت این پارامتر رو تنظیم کن.
سوالات متداول درباره پکیج DBMS_LOCK در اوراکل
DBMS_LOCK برای تعریف و کنترل قفلهای منطقی توسط کاربر (User-defined Locks) در Oracle بهکار میرود.
زمانی که بخواهیم فقط یک session در آنِ واحد یک فرآیند خاص را اجرا کند، یا بخواهیم مانع اجرای همزمان چند پردازش حساس شویم،
از این پکیج استفاده میکنیم. این قفلها مستقل از قفلهای استاندارد اوراکل هستند و مدیریتشان در اختیار برنامهنویس است.
قفلهای خودکار مثل Row-level یا Table-level Locks، توسط Oracle در هنگام اجرای کوئریها (مانند UPDATE یا SELECT FOR UPDATE) ایجاد میشوند و بر اساس مدل ACID عمل میکنند.
اما DBMS_LOCK به برنامهنویس اجازه میدهد بهصورت دستی و منطقی قفلهایی مستقل از دیتابیس بسازد تا اجرای همزمان کنترل شود، بدون اینکه نیازی به قفل فیزیکی روی رکورد یا جدول باشد.
اگر سشن قطع شود (مثلاً کاربر disconnect کند یا session crash کند)، قفل ایجادشده توسط DBMS_LOCK بهطور خودکار توسط Oracle آزاد خواهد شد.
همچنین میتوان با تنظیم مقدار expiration_secs در ALLOCATE_UNIQUE، عمر قفل را محدود کرد تا بعد از مدتی حتی بدون release کردن هم منقضی شود.
کد ۱ در خروجی DBMS_LOCK.REQUEST نشاندهندهی timeout است.
یعنی session تلاش کرده قفل را بگیرد ولی چون قفل در دست یک session دیگر بوده و تا زمان مشخصشده (مثلاً ۵ ثانیه) قفل آزاد نشده، درخواست رد شده است.
برای مدیریت بهتر، بررسی کد بازگشتی (return code) ضروری است.
نتیجهگیری
پکیج DBMS_LOCK در Oracle یک ابزار قدرتمند برای مدیریت قفلهای سفارشی و هماهنگسازی پردازشهای همزمانه.
اگر درست استفاده بشه، میتونه از بسیاری از مشکلات race condition و اجرای همزمان ناخواسته جلوگیری کنه.
اگه در پروژهای نیاز به کنترل دقیقتر بر concurrency داری، یادگیری این پکیج واجبه!
📢 نظر شما چیست؟ اگر شما هم اطلاعات و تجربه خوبی در استفاده از پکیج DBMS_LOCK دارید خوشحال میشم در بخش نظرات، تجربه های ارزشمندتان را با ما به اشتراک بگذارید! 🚀
سؤالی درباره این مقاله داری؟
اگر نکتهای در این مقاله برات مبهم بود یا خواستی بیشتر بدونی، همین حالا برام بنویس تا دقیق و صمیمی پاسخت رو بدم — مثل یه گفتوگوی واقعی 💬
برو به صفحه پرسش و پاسخ
دیدگاهتان را بنویسید