數(shù)據(jù)庫中的事務(wù)與并發(fā)問題的案例講解
發(fā)表時間:2023-08-24 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]數(shù)據(jù)庫中的事務(wù)和并發(fā)問題探討引子最近有同事寫了段代碼,負(fù)責(zé)創(chuàng)建訂單的邏輯,代碼審查時發(fā)現(xiàn)可能會有并發(fā)的問題。同事并不認(rèn)同,他認(rèn)為他的邏輯是寫在存儲過程中的,應(yīng)該沒有問題。代碼的邏輯大概是(偽代碼):begin transactionif 查詢到客戶存在進(jìn)行中的訂單rollback transact...
數(shù)據(jù)庫中的事務(wù)和并發(fā)問題探討
引子
最近有同事寫了段代碼,負(fù)責(zé)創(chuàng)建訂單的邏輯,代碼審查時發(fā)現(xiàn)可能會有并發(fā)的問題。同事并不認(rèn)同,他認(rèn)為他的邏輯是寫在存儲過程中的,應(yīng)該沒有問題。
代碼的邏輯大概是(偽代碼):
begin transaction
if 查詢到客戶存在進(jìn)行中的訂單
rollback transaction
if 查詢到設(shè)備存在進(jìn)行中的訂單
rollback transaction
插入訂單
commit transaction
下面針對這個邏輯進(jìn)行分析,為什么這個事務(wù)會出現(xiàn)并發(fā)問題。
事務(wù)概述
首先,提出兩個問題,然后帶著問題討論事務(wù)相關(guān)的知識點,最后來解決這兩個問題并回答前文的問題。
第一個問題,事務(wù)是否可以并發(fā)?
第二個問題,數(shù)據(jù)庫是怎么隔離事務(wù)的?
事務(wù)的表現(xiàn)特性
數(shù)據(jù)庫中執(zhí)行事務(wù)涉及到很多方面,包括如何處理臨界資源,如何加鎖解鎖等等。但是無論事務(wù)如何執(zhí)行,都需要保證以下幾個特性:
原子性
一致性
隔離性
持久性
原子性:所有的操作是一個邏輯單元,要么都提交成功,要么就都失敗;
一致性:只有合法的數(shù)據(jù)被寫入數(shù)據(jù)庫,否則事務(wù)回滾到最初的狀態(tài);
隔離性:允許多個事務(wù)同時進(jìn)行,而不會破壞數(shù)據(jù)的正確性和完整性;
持久性:事務(wù)結(jié)束后,已經(jīng)提交的結(jié)果被固化保存。
數(shù)據(jù)庫的各種鎖
共享鎖
共享鎖用于非獨占的業(yè)務(wù),允許多個事務(wù)同時讀取鎖定的資源,但是不允許資源被更新。
加鎖時機:執(zhí)行select語句時默認(rèn)會被加上
解鎖時機:執(zhí)行完讀取后默認(rèn)解除
與其他鎖兼容性:數(shù)據(jù)上被設(shè)置了共享鎖,則不會允許再增加共享鎖和獨占鎖
并發(fā)性能:具有良好的并發(fā)性能
排他鎖
排他鎖,也叫獨占鎖。顧名思義,被排他鎖鎖定的資源不會允許其他事務(wù)進(jìn)行任何操作。
加鎖時機:執(zhí)行insert,update,delete時默認(rèn)會被加上
解鎖時機:事務(wù)結(jié)束才能解除
兼容性:如果數(shù)據(jù)上有其他鎖,不能增加獨占鎖;同樣獨占鎖存在時也不會允許增加其他鎖
并發(fā)性能:其他事務(wù)必須等待前一個事務(wù)結(jié)束后才能執(zhí)行,不能并發(fā),只能串行
更新鎖
在更新的初始階段用于鎖定所需要的資源,防止在讀取階段使用共享鎖造成死鎖。
加鎖時機:執(zhí)行update時,使用更新鎖鎖定相關(guān)資源
解鎖時機:讀取完畢,執(zhí)行更新操作時,更新鎖升級為獨占鎖
兼容性:更新鎖與共享鎖兼容,即可以同時存在更新鎖和共享鎖,但只能有一個更新鎖
并發(fā)性能:更新初期的讀取階段可以允許其他事務(wù)讀取資源,允許有限的并發(fā);后期對資源進(jìn)行獨占時不允許并發(fā)。
事務(wù)隔離級別
通用的事務(wù)隔離級別有四種,SQL Server還有另外擴展出來的級別,在此不多介紹。
Serializable(串行化)
工作方式類似于可重復(fù)讀。但它不僅會鎖定受影響的數(shù)據(jù),還會鎖定這個范圍。這就阻止了新數(shù)據(jù)插入查詢所涉及的范圍,這種情況可以導(dǎo)致幻像讀。
Repeatable Read(可重復(fù)讀)
像已提交讀級別那樣讀數(shù)據(jù),但會保持共享鎖直到事務(wù)結(jié)束。
Read Commit
只讀取提交的數(shù)據(jù)并等待其他事務(wù)釋放排他鎖。讀數(shù)據(jù)的共享鎖在讀操作完成后立即釋放。已提交讀是SQL Server的默認(rèn)隔離級別。
Read Uncommited
在讀數(shù)據(jù)時不會檢查或使用任何鎖。因此,在這種隔離級別中可能讀取到?jīng)]有提交的數(shù)據(jù)。
回答前文的問題
第一個問題,事務(wù)是否可以并發(fā)?
答案是肯定的,數(shù)據(jù)庫中為了提高性能,允許同時進(jìn)行多個事務(wù)操作,這個事務(wù)跟發(fā)起方式無關(guān),使用存儲過程發(fā)起,或者使用代碼發(fā)起,又或者使用普通的SQL語句發(fā)起并沒有什么區(qū)別。
第二個問題,數(shù)據(jù)庫是怎么隔離事務(wù)的?
要回答這個問題,先要理解數(shù)據(jù)庫中的鎖機制和數(shù)據(jù)庫事務(wù)隔離級別。數(shù)據(jù)庫中的鎖可以分為三種類型:共享鎖、獨占鎖和更新鎖。使用不同級別的鎖并配合不同的鎖定范圍已達(dá)到不同的事務(wù)隔離級別并在此基礎(chǔ)上并發(fā)或串行執(zhí)行事務(wù)。
第三個問題,為什么本文開頭的事務(wù)會存在并發(fā)問題?
因為事務(wù)的開始執(zhí)行的是select,select使用的是共享鎖,有可能并發(fā)的事務(wù)在同一時間執(zhí)行select導(dǎo)致同時認(rèn)為自己都是合法操作,而排隊執(zhí)行后續(xù)的事務(wù)。結(jié)果導(dǎo)致了實際上就有可能插入重復(fù)的數(shù)據(jù),比如只剩下一個商品,卻創(chuàng)建了兩個銷售訂單。
如何防止并發(fā)問題
在事務(wù)中
根據(jù)前文所講,使用insert,update或delete可以在默認(rèn)事務(wù)級別人為造成事務(wù)串行化,因此可以在事務(wù)內(nèi)部一開始都使用update更新一條公共的數(shù)據(jù),這樣的話同類型的事務(wù)都會串行化,然后再增加一個判斷語句,用于判斷后續(xù)的事務(wù)內(nèi)容是否應(yīng)該執(zhí)行。這樣足以確保所有的操作都按照合理合法,唯一的缺點是可能造成性能問題。
在事務(wù)外
現(xiàn)在分布式的系統(tǒng)越來越多,但是再分布的系統(tǒng)也會有些共享資源,比如redis或zookeeper,可以利用redis或者zookeeper造一些分布式的鎖(此類屬于其他博文內(nèi)容,在此不再展開)。利用事務(wù)外部的鎖將同類型的事務(wù)做一些串行化處理,再配合事務(wù)內(nèi)部的檢查機制,足以確保解決事務(wù)的并發(fā)問題。
參考資料
事務(wù)并發(fā)的問題及處理
數(shù)據(jù)庫事務(wù)的四大特性以及事務(wù)的隔離級別
數(shù)據(jù)庫事務(wù)和并發(fā)
SQLServer事務(wù)的隔離級別
以上就是數(shù)據(jù)庫中的事務(wù)和并發(fā)問題的實例講解的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
學(xué)習(xí)教程快速掌握從入門到精通的SQL知識。