對(duì)于用戶甲來(lái)說(shuō),他的動(dòng)作稍微比乙快一點(diǎn)點(diǎn),其購(gòu)買過(guò)程所觸發(fā)的動(dòng)作大致是這樣的:
1. SELECT book_number FROM book WHERE book_id = 123;
book_number大于零,確認(rèn)購(gòu)買行為并更新book_number
2. UPDATE book SET book_number = book_number – 1 WHERE book_id = 123;
購(gòu)書成功
而對(duì)于用戶乙來(lái)說(shuō),他的動(dòng)作稍微比甲慢一點(diǎn)點(diǎn),其購(gòu)買過(guò)程所觸發(fā)的動(dòng)作和甲相同:
1. SELECT book_number FROM book WHERE book_id = 123;
這個(gè)時(shí)候,甲剛剛進(jìn)行完第一步的操作,還沒(méi)來(lái)得及做第二步操作,所以book_number一定大于零
2. UPDATE book SET book_number = book_number – 1 WHERE book_id = 123;
購(gòu)書成功
表面上看甲乙的操作都成功了,他們都買到了書,但是庫(kù)存只有一本,他們?cè)趺纯赡芏汲晒δ兀吭倏纯磾?shù)據(jù)表里book_number的內(nèi)容,已經(jīng)變成“-1”了,這當(dāng)然是不能允許的(實(shí)際上,聲明這樣的列類型應(yīng)該加上unsigned的屬性,以保證其不能為負(fù),這里是為了說(shuō)明問(wèn)題所以沒(méi)有這樣設(shè)置)
好了,問(wèn)題陳述清楚了,再來(lái)看看怎么利用事務(wù)來(lái)解決這個(gè)問(wèn)題,打開MySQL手冊(cè),可以看到想用事務(wù)來(lái)保護(hù)你的SQL正確執(zhí)行其實(shí)很簡(jiǎn)單,基本就是三個(gè)語(yǔ)句:開始,提交,回滾。
開始:START TRANSACTION或BEGIN語(yǔ)句可以開始一項(xiàng)新的事務(wù)
提交:COMMIT可以提交當(dāng)前事務(wù),是變更成為永久變更
回滾:ROLLBACK可以回滾當(dāng)前事務(wù),取消其變更
此外,SET AUTOCOMMIT = {0 | 1}可以禁用或啟用默認(rèn)的autocommit模式,用于當(dāng)前連接。
那是不是只要用事務(wù)語(yǔ)句包一下我們的SQL語(yǔ)句就能保證正確了呢?比如下面代碼:
答案是否定了,這樣依然不能避免問(wèn)題的發(fā)生,如果想避免這樣的情況,實(shí)際應(yīng)該如下:
由于加入了FOR UPDATE,所以會(huì)在此條記錄上加上一個(gè)行鎖,如果此事務(wù)沒(méi)有完全結(jié)束,那么其他的事務(wù)在使用SELECT … FOR UPDATE請(qǐng)求的時(shí)候就會(huì)處于等待狀態(tài),直到上一個(gè)事務(wù)結(jié)束,它才能繼續(xù),從而避免了問(wèn)題的發(fā)生,需要注意的是,如果你其他的事務(wù)使用的是不帶FOR UPDATE的SELECT語(yǔ)句,將得不到這種保護(hù)。
以上的相關(guān)內(nèi)容就是對(duì)MySQL數(shù)據(jù)庫(kù)與事務(wù)的介紹,望你能有所收獲。