PDA

View Full Version : نحوه قفل کردن دیتابیس از داخل برنامه



Jason.Bourne
یک شنبه 09 تیر 1392, 14:01 عصر
چطوری میشه از طریق برنامه ای که می نویسیم بر روی پایگاه داده قفل بگذاریم تا اجازه دسترسی همزمان به یک رکورد از پایگاه داده را از کاربر صلب کنیم.

البته بجز روشی که یک فیلد به عنوان lock برای رکورد در پایگاه داده تعیین می کنیم و از تغییر مقدار آن متوجه می شویم که کاربر دیگری این رکورد را در اختیار خود گرفته؟!

Jason.Bourne
دوشنبه 10 تیر 1392, 09:29 صبح
کسی نظری ندارد؟

MMSHFE
دوشنبه 10 تیر 1392, 09:40 صبح
تنها راه منطقی، همون Lock هست، منتها نه بصورت فیلد، بلکه با دستور LOCK TABLE خود MySQL

Jason.Bourne
دوشنبه 10 تیر 1392, 10:05 صبح
شنیده ام که نوعی قفل کردن هم هست که از طریق MySQL انجام میگیرد. البته به غیر از LOCK TABLE.
البته اون نوعی که مد نظر من هست ظاهرا یک عملیات خاص را Lock می کند (مثلا محدوده ای از برنامه که عملیات خاصی را انجام می دهد).

eshpilen
دوشنبه 10 تیر 1392, 13:09 عصر
تابع get_lock هم در MySQL وجود داره که ازش میشه برای موارد دیگر و مثلا شبیه سازی قفل در سطح رکورد هم استفاده کرد.

LOCK TABLE کل جدول رو قفل میکنه. یعنی حتی اگر شما فقط با یک رکورد خاص کار دارید و مشکلی نداره همزمان رکوردهای دیگری در جدول خونده بشن، تا زمانی که کار شما تموم بشه و قفل آزاد بشه درخواستهای دیگر باید منتظر بمونن.

البته درمورد get_lock باید توجه داشت که این یک Advisory lock است. یعنی صرفا بصورت قراردادی در کدهایی که ازش استفاده بکنن کاربرد داره. یعنی اگر کدی بدون اینکه get_lock مورد نظر رو اجرا بکنه بخواد به جدول یا رکورد مورد نظر دسترسی پیدا کنه، چیزی جلوش رو نمیگیره.

راستی بسته به هدف شما از این کار، احتمالا میتونید از Transaction هم بجای این قضیهء قفل و اینها استفاده کنید.
البته MyISAM ترنزکشن نداره و بنابراین باید از انجین دیگری مثل InnoDB استفاده کنید.

eshpilen
دوشنبه 10 تیر 1392, 13:20 عصر
راستی اگر از get_lock استفاده کردی بگو یه نکتهء امنیتی هم داره بهت بگم :چشمک:

Jason.Bourne
دوشنبه 10 تیر 1392, 15:37 عصر
میشه مثالی از get_lock و Transactionمطرح کنید؟
البته کارائی Transaction را میدونم اما کنجکاو شدم که در چه حالتی میشه از Transactionدر زمینه قفل کردن استفاده کرد.

ممنون

shahriyar3
دوشنبه 10 تیر 1392, 16:13 عصر
فکر کنم trigger هم همچین خاصیتی رو داشته باشه.

eshpilen
پنج شنبه 13 تیر 1392, 11:10 صبح
میشه مثالی از get_lock و Transactionمطرح کنید؟
البته کارائی Transaction را میدونم اما کنجکاو شدم که در چه حالتی میشه از Transactionدر زمینه قفل کردن استفاده کرد.

ممنون
خب البته ترنزکشن فکر میکنم در مواردی استفاده میشه که میخوایم یکسری کوئری پشت سرهم اجرا بشن و بین اونها کوئری دیگری اجرا نشه. البته یک خاصیت دیگرش هم اینه که در نهایت یا همهء کوئری ها با موفقیت اجرا میشن و یا همه انگار که اجرا نشدن.
در مورد اول که میخوایم کوئری دیگری بین چند کوئری (یا حتی بعضی وقتا بین مراحل اجرای یک کوئری) اجرا نشه میشه از قفل هم استفاده کرد.
ولی اگر قفل رو برای منظور دیگری میخواید، یعنی میخواید دقیقا در اون زمان خاص که شما دستور میدید، کوئری های دیگری نتونن اجرا بشن، فکر نمیکنم از ترنزکشن بشه استفاده کرد.

اینم درمورد get_lock:

گرفتن قفل:


select get_lock('lock1', -1)

الان این یا قفل رو میگیره (به شرط اینکه قفلی با نام lock1 درحال حاضر توسط پردازش/کدهای دیگری گرفته نشده باشه) یا اینکه منتظر میمونه تا قفل lock1 که توسط کدهای دیگری گرفته شده آزاد بشه و بعدش میتونه قفل رو بگیره. البته اگر تعداد درخواستها برای گرفتن قفل زیاد باشه، به ترتیب زمان بینشون اولویت بندی میشه. بهرحال در هر زمان فقط یک قفل با این نام (lock1) میتونه توسط هر پردازشی که پیشدستی کرده باشه اشغال باشه.

بعد شما موقعی که کارتون تموم شده میتونید قفل رو به این شکل آزاد کنید:


select release_lock('lock1')

البته وقتی اجرای اسکریپت تموم بشه و اتصال دیتابیس هم بصورت خودکار بسته میشه، قفل ها هم بصورت خودکار آزاد میشن، ولی اغلب بهتره بخاطر افزایش پرفورمنس و مسائل آینده، قفل رو هروقت کارتون باهاش تموم شد آزاد کنید.

نکته ای که این وسط هست اینه که مثلا این قفل با اسم lock1 در تمامی سرور MySQL بصورت گلوبال موجوده و اثر داره. بنابراین اگر کس دیگری حتی از سایت دیگری که روی هاست مشترکی با شماست و از سرور مای اس کیوال مشترکی استفاده میکنه قفلی با همین نام داشته باشه، با برنامهء شما تداخل میکنه. و حتی ممکنه کسی عمدا قفل همنامی رو بگیره و آزاد نکنه تا برنامهء شما قادر به اجرا نباشه!!
بنابراین شما میتونید مثلا یک رشتهء رندوم طولانی ولی ثابت رو به اسم قفلهای خودتون اضافه کنید تا کسی نتونه اسم قفل های برنامهء شما رو بفهمه و سوء استفاده کنه؛ البته این از همنامی و تداخل تصادفی هم جلوگیری میکنه.

eshpilen
پنج شنبه 13 تیر 1392, 11:20 صبح
حالا اون آرگومان دوم (عدد ‎-1) تابع get_lock یک چیه.
خب اون Timeout رو مشخص میکنه.
من بهش منفی یک دادم یعنی Timeout نداره و برنامه میتونه در همون نقطه تا ابد منتظر آزاد شدن قفل مورد نظر بمونه.
ولی اگر عدد دیگری بدید، مثلا 10، بهش میگید که اگر تا 10 ثانیه موفق نشد قفل رو بگیره، تابع برگرده و در نتیجه بقیهء برنامه اجرا بشه.
از مقداری که select برگشت میده هم میشه فهمید که قفل با موفقیت گرفته شده یا نه. اگر موفق شده باشه، عدد 1 رو در نتیجهء کوئری برگشت میده، اگر موفق نباشه، عدد صفر برمیگرده.

Jason.Bourne
پنج شنبه 13 تیر 1392, 12:53 عصر
به عبارتی ما توسط advisory lock عملیات هایی که بین get_lock و release_lock وجود دارند را مدیریت می کنیم. یعنی همیشه فقط یک کاربر هست که عملیات ما بین
get_lock و release_lock را در حال اجرا دارد. درست متوجه شدم؟

eshpilen
پنج شنبه 13 تیر 1392, 18:56 عصر
بله همینطوره.
البته به شرطی که اسم قفل یکسان باشه.
ضمنا به پارامتر دوم هم توجه داشته باشید که اگر غیر از منفی یک باشه و Timeout بشه تابع قبل از اینکه قفل آزاد بشه برمیگرده و دستورات بعد از اون میتونن اجرا بشن.

Jason.Bourne
جمعه 14 تیر 1392, 12:54 عصر
بله همینطوره.
البته به شرطی که اسم قفل یکسان باشه.
ضمنا به پارامتر دوم هم توجه داشته باشید که اگر غیر از منفی یک باشه و Timeout بشه تابع قبل از اینکه قفل آزاد بشه برمیگرده و دستورات بعد از اون میتونن اجرا بشن.

چطور میشه براش Timeout تعیین کرد و اجازه هم نداد که در صورت Timeout دستورات ما بین گرفتن قفل و آزاد کردن قفل اجرا نشوند؟

Jason.Bourne
جمعه 14 تیر 1392, 13:03 عصر
بله همینطوره. البته به شرطی که اسم قفل یکسان باشه. ضمنا به پارامتر دوم هم توجه داشته باشید که اگر غیر از منفی یک باشه و Timeout بشه تابع قبل از اینکه قفل آزاد بشه برمیگرده و دستورات بعد از اون میتونن اجرا بشن. چطور میشه براش Timeout تعیین کرد و اجازه هم نداد که در صورت Timeout دستورات ما بین گرفتن قفل و آزاد کردن قفل اجرا نشوند؟

eshpilen
شنبه 15 تیر 1392, 07:40 صبح
چطور میشه براش Timeout تعیین کرد و اجازه هم نداد که در صورت Timeout دستورات ما بین گرفتن قفل و آزاد کردن قفل اجرا نشوند؟
قبلا گفتم که:


از مقداری که select برگشت میده هم میشه فهمید که قفل با موفقیت گرفته شده یا نه. اگر موفق شده باشه، عدد 1 رو در نتیجهء کوئری برگشت میده، اگر موفق نباشه، عدد صفر برمیگرده.

Jason.Bourne
شنبه 15 تیر 1392, 12:09 عصر
من یه مشکل دارم.
وقتی که سعی میکنم با release_lock یک قفل را آزاد کنم، مقدار 0 برگردانده می شود و بعد از آن اگر دوباره بخواهم با GET_LOCK قفلی را بگیرم با اینکه timeout را 1- تعیین کرده ام، اما قفل گرفته نمی شود و مقدار 0 برگشت داده می شود.
باید چکار کنم؟

در ضمن MySQL برای تعداد قفل های فعال محدودیتی ندارد؟