داشتم روی پروژه کار میکردم که یهو یادم افتاد ای داد بیداد دسترسی همزمان و نیاز به قفل کردن رو فراموش کردم!
گفتم کارم درآمد الان باید کلی کد دیگه و الگوریتم پیچیده پیاده کنم؛ ولی خوشبختانه فقط یک خط به دوتا فایل اضافه کردم و بنظرم نیازی نیست خیلی درگیر جزییات بشم.
فکر میکنم خیلی افراد از lock در جاهایی که باید استفاده نمیکنن که دلیلش میتونه فراموشی و یا اصلا عدم اطلاع از لزوم این قضیه باشه.
بهتره با یه مثال عملی توضیح بدم.
فرضا من در بخشی از برنامهء خودم وقتی کاربر یک تلاش ناموفق برای لاگین داره این کوئری رو اجرا میکنم:
select * from `failed_logins` where `username`=$_username limit 1
بعد اگر رکوردی برای تلاشهای ناموفق اون کاربر در دیتابیس وجود نداشت، در چند خط بعد با کوئری زیر یک رکورد برای ذخیرهء تلاشهای ناموفق اون نام کاربری در جدول failed_logins درج میکنم:
insert into `failed_logins` (`username`, `times`, `pos`) values($_username, $times, $pos)
خب الان این چه مشکلی داره؟
مشکل اینه که ممکنه در فاصلهء زمانی بعد از اجرای کوئری select تا قبل از اجرای کوئری insert، یک رکورد مشابه (برای همین کاربر) توسط درخواست دیگری در دیتابیس درج شده باشه. و مسلما این یک باگ است!
خب راه حل چیه؟
قبل از اجرای کوئری select این کوئری رو اجرا میکنیم:
lock tables `failed_logins` write
این کوئری باعث میشه جدول failed_logins قفل بشه و تا وقتی اجرای پردازش فعلی ما تموم نشده (یا کانکشن به دیتابیس بسته نشده) یا با کوئری unlock tables قفل رو آزاد نکردیم، کوئری های پردازشهای دیگر که با جدول failed_logins کار دارن چه از نوع خواندن (select) و چه از نوع نوشتن (update، insert، ...) در صف انتظار برای اجرا باقی بمونن تا کار ما تموم بشه.
البته من مثال رو ساده کردم و تنها یک مورد کوچک رو هم گفتم. این دستور آپشن های بیشتری داره و قفل ها انواع مختلفی دارن و هر نوع storage engine هم انواع مختلفی از قفل رو پشتیبانی میکنن (مثلا در بعضی ها میشه بجای کل جدول فقط یک سطر رو قفل کرد).
خب شما هم میتونید اطلاعات و تجربیات خودتون در ارتباط با قفل های مربوط به عملیات دیتابیس رو بگید. و احتمالا تازه متوجه میشید که خیلی جاها باید از قفل استفاده میکردید و نکردید!!