PDA

View Full Version : آموزش تراکنش کردن در MYSQL



i-php-i
چهارشنبه 06 اسفند 1393, 02:27 صبح
با سلام به دوستان عزیز

می تونم بگم این تاپیک اولین مقاله آموزشی من توی وب سایت برنامه نویس هست و امیدوارم مقاله مفیدی باشه

توی نرم افزارهایی که با پول سروکار داره ممکنه حالتهایی پیش بیاد که نیاز هست چندین کوئری بزنیم تا پول رو از حساب یک نفر کم کنیم و به حساب یه نفر دیگه اضافه کنیم. در این حالت کوئری های ما به این صورت می تونه باشه:

UPDATE `user_mony` SET `mony`=mony-1500000 WHERE (`id`='A')

UPDATE `user_mony` SET `mony`=mony+1500000 WHERE (`id`='B')

در حالت عادی این کوئری ها درست کار می کنن ولی فرض کنید کوئری اول رو زدیم و بعد سیستم خاموش شد! یا کوئری اول رو زدیم و قبل از کوئری دوم، کاربر B از سیستم حذف بشه.

در هر دو حالت 150 هزار تومن به کاربر A ضرر زدیم!

برای حل این مشکل سیستم ایزالاسیون Isolation Levels طراحی شده که از 4 سطح مختلف تشکیل می شه

لازم به ذکر هست که این سیستم روی جدول نوع InnoDB اجرا می شه و روی جدولهای نوع MyISAM کاربرد نداره.


Read uncommitted

در این حالت تراکنش در حال اجرا می تونه نتیجه تراکنش های انجام نشده رو ببینه! نمی دونم چطور توضیحش بدم، فقط می تونم بگم بهتره این روش رو هیچوقت استفاده نکنید!

Read committed

در این حالت تراکنش در حال اجرا می تونه نتیجه های تراکنش های انجام شده رو ببینه و اطلاعاتی که به شما می ده واقعی هستن اما یه مشکل داره و اونم اینکه ردیفهایی که در شرط تراکنش صدق می کنن رو قفل نمی کنه.

برای توضیح بهتر این مشکل این دو کوئری رو مثال می زنم که مدیر می خواد تعداد کاربرهای طلبکار وب سایت خودش رو بدست بیاره (فرض می کنیم کلا 10 کاربر داریم که مدیر وب سایت باید به اونها پول پرداخت کنه)

SELECT * FROM `usertb` WHERE (`id`='ok')

SELECT * FROM `usertb` WHERE (`id`='ok')
بنظر می رسه که هر دوکوئری باید جواب یکسانی رو برگردونن اما فرض کنید بعد از کوئری اول، یکی از کاربرها حساب کاربری خودش رو حذف می کنه و بعد کوئری دوم اجرا می شه!

در این حالت نمی دونیم به مدیر بگیم 10 تا کاربر طلبکار داری یا 9 تا! و طبیعتا نمی تونیم مبلغ بدهی وب سایت رو دقیقا مشخص کنیم.

Repeatable read

این سطح از ایزولاسیون بر حل مشکل فوق ایجاد شده و در این سطح همه ردیف هایی که در شرط کوئری ها صدق می کنن قفل می شن تا جلوی مشکلاتی که توضیح داده شد گرفته بشه

در این حالت درسته که ردیفهایی که در شرط صدق می کنن قفل می شن ولی ممکنه بعد از کوئری اول یه کاربر طلبکار جدید به لیست طلبکارها اضافه INSERT بشه و چون تا حالا وجود نداشته طبیعتا نمی شد روش قفل گذاشت!

به همین دلیل کوئری دوم طلب این کاربر جدید رو محاسبه می کنه و باز هم عددی که به دست میاد با کوئری اول تفاوت داره

Serializable

در این حالت علاوه بر قفل شدن ردیفهایی که در شرطها صدق می کنن از INSERT جدید جلوگیری می شه تا اشتباهی پیش نیاد.

برای اینکه Serializable درست کار کنه باید تراکنش ها رو طوری مرتب کنه که با هم تداخل نداشته باشن و به همین دلیل از وقفه های زمانی استفاده می کنه و به همین دلیل باعث می شه نرم افزار کمی کند بشه اما پایداری داده ها رو تظمین می کنه!

نکته:

Read committed ایزولاسیون پیش فرض هست و برای تغییر سطح ایزولاسیون باید از این کوئری استفاده کنیم:


SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

حالا می تونیم با خیال راحت تراکنش کنیم


SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
START TRANSACTION
UPDATE `user_mony` SET `mony`=mony-1500000 WHERE (`id`='A')
UPDATE `user_mony` SET `mony`=mony+1500000 WHERE (`id`='B')
COMMIT


ROLLBACK

گاهی کار پیچیده می شه و باید چندین کوئری مختلف رو بزنیم و محسابات مختلفی رو انجام بدیم تا تصمیم بگیریم پولی جابجا بشه یا نه

اگر محاسبات درست بود می تونیم با استفاده از COMMIT تراکنش رو تکمیل کنیم ولی اگر محاسبات اونی که انتظار داشتیم نبود خیلی ساده می تونیم با استفاده از ROLLBACK همه کوئری هایی که توی تراکنش استفاده کردیم رو به حالت قبل برگردونیم

به این صورت

if($has_error=='yes'){
ROLLBACK
}
else{
COMMIT
}

فرزند کوروش
پنج شنبه 07 اسفند 1393, 19:37 عصر
اگه ممکنه يه مثال عملي هم بزن
ممنون