1.并發沖突
當兩個進程試圖在同一時間修改同一數據,就會產生沖突。
2.并發控制
有兩種方式管理并發數據訪問:樂觀并發控制、悲觀并發控制。
這兩種控制模式的區別在于,是在沖突發生前進行防止,還是在發生后采用某種方法來處理沖突。
3.悲觀并發控制
悲觀并發模式假定系統中存在足夠多的數據修改操作,以致任何確定的讀操作都可能會受到由別的用戶所制造的數據修改的影響。
也就是說,悲觀并發模式假定沖突總是會發生的。
悲觀并發控制是通過獨占正在被讀取的數據來避免沖突。
但是獨占數據會導致其它進程無法修改該數據,進而產生阻塞——讀數據和寫數據會互相阻塞。
4.樂觀并發控制
樂觀并發模式假定系統的數據修改操作只會生產非常少的沖突,也就是說任何進程都不太可能修改別的進程正在訪問的數據。
樂觀并發模式下,讀數據和寫數據之間不會發生沖突,只有寫數據與寫數據之間會發生沖突。即讀數據不會產生阻塞,只有寫數據才會產生阻塞。
5.并發沖突生產的問題
5.1.丟失更新(Lost updates)
兩個進程同時讀取一筆數據,然后進行修改,那么后提交的數據會覆蓋先提交的數據。
如果數據允許覆蓋式更新(比如用戶姓名),那么丟失更新并不算太大的問題,如果數據是累加式更新(比如庫存數量),那么丟失更新是非常嚴重的問題,并且在非并發模式下無法重復問題的發生。
5.2.臟讀(Dirty reads)
當一個進程更新了數據,但(事務)未提交,這時候另一個進程讀取同一筆數據,如果前一個進程取消了更新(事務回滾),那么后一個進程讀取的就是臟數據。
臟讀會產生嚴重的問題,在任何情況下都是不允許的。
5.3.不可重復讀(Non-repeatable reads)
當一個進程讀取了一筆數據后,另一個進程更新了同一筆數據,然后第一個進程再次讀取同一筆數據,卻得到了與第一次讀取不同的結果。
在事務A更新記錄之后(update Customers set Name = 'B' where Name = 'A'),事務B讀取相同記錄(select Name form Customers where Name = 'A'),但事務B拿到的是事務A更新之后的數據(Customers.Name的值為'B'),在事務B讀取記錄之后,事務A進行了事務回滾(Customers.Name的值為'A'),導致事務B的數據是不真實的。
5.4.幻讀(Phantoms)
幻讀與臟讀的相似之處在于:兩者都是兩次讀取的結果不一致。
不同之處在于:幻讀是兩次讀取的記錄數量不一致,而臟讀是兩次讀取的記錄的數據不一致。
事務A讀取記錄之后(select * from Customers where Name like 'A%'),事務B又插入了符合事務A讀取條件的新記錄(insert into Customers(Name) values('AAA')),那么當事務A再用相同條件讀取記錄時,得到的集合卻與上一次讀取不同(多了記錄)。
6.隔離級別
SQL Server2005支持5種隔離級別來控制沖突。其中三種只在悲觀并發模式中使用,一種只在樂觀并發模式中使用,另一個可以在兩種模式中使用。
6.1.未提交讀(Uncommitted Read)
未提交讀只能防止“丟失更新”問題,其它問題不能防止。
未提交讀是針對阻塞太頻繁的悲觀并發控制,因為它只是忽略了鎖,而不保障事務的一致性。
6.2.已提交讀(Read Committed)
已提交讀既可以是樂觀的也可以是悲觀的,這取決于數據庫的read_committed_snapshot設置。默認情況下這個選項是關閉的,所以該隔離級別默認情況下是采用悲觀并發控制。
已提交讀可以防止臟讀問題。
6.3.可重復讀(Repeatable Read)
可重復讀是一種悲觀的隔離級別。它在已提交讀的基礎上增加了新特性:確保當事務重新訪問數據或查詢被再一次執行時,數據將不會再發生改變。
可重復讀不但可以防止臟讀問題,還可以防止不可重復讀問題,但是不能防止幻讀問題。
注意,可重復讀的資源開銷是很大的,事務中所有的數據必須等待事務完成之后才能訪問。
6.4.快照(Snapshot)
快照是一種樂觀隔離級別。
Snapshot事務中任何語句所讀取的記錄,都是事務啟動時的數據。
這相當于事務啟動時,數據庫為事務生成了一份專用“快照”。
在當前事務中看到不其它事務在當前事務啟動之后所進行的數據修改。
Snapshot事務不會讀取記錄時要求鎖定,讀取記錄的Snapshot事務不會鎖住其它事務寫入記錄,寫入記錄的事務也不會鎖住Snapshot事務讀取數據。
快照隔離級別的事務不是串行執行的,兩個進程同時使用快照隔離,如果它們執行多次,可能最終產生的結果不會一致。(這段話要證實)
6.5.可串行化(Serializable)
可串行化是一種悲觀隔離級別。它在可重復讀的基礎上增加了新的特性:確保在兩次查詢的中間,不會增加新的行。
可串行化是最健壯的悲觀隔離級別,因為它防止了并發沖突產生的4個問題。
可串行化也是資源開銷最大的措施。當使用可串行化隔離時,如果SQL的條件字段沒有索引,那么SQL Server會產生表級鎖。
6.6.總結
7.鎖
7.1.死鎖
當二或多個工作各自具有某個資源的鎖定,但其它工作嘗試要鎖定此資源,而造成工作永久封鎖彼此時,會發生死鎖。例如:
1.事務A取得數據列1的共享鎖定。
2.事務B取得數據列2的共享鎖定。
3.事務A現在要求數據列2的獨占鎖定,但會被封鎖直到事務B完成并釋出對數據列2的共享鎖定為止。
4.事務B現在要求數據列1的獨占鎖定,但會被封鎖直到事務A完成并釋出對數據列1的共享鎖定為止。
等到事務B完成后,事務A才能完成,但事務B被事務A封鎖了。這個狀況也稱為「循環相依性」(Cyclic Dependency)。事務A相依于事務B,并且事務B也因為相依于事務A而封閉了這個循環。
例如以下操作就會產生死鎖,兩個連接互相阻塞對方的update。
連接1:
begin tran
select * from customers
update customers set CompanyName = CompanyName
select * from Employees
–因為Employees被連接2鎖住了,所以這里會阻塞。
update Employees set LastName = LastName
commit tran
連接2:
begin tran
select * from Employees
update Employees set LastName = LastName
waitfor delay '00:00:05'
select * from customers
--因為customers被連接1鎖住了,所以這里會阻塞。
update customers set CompanyName = CompanyName
commit tran
SQL Server遇到死鎖時會自動殺死其中一個事務,而另一個事務會正常結束(提交或回滾)。
SQL Server對殺死的連接返回錯誤代碼是1205,異常提示是:
Your transaction (process ID #52) was deadlocked on {lock | communication buffer | thread} resources with another process and has been chosen as the deadlock victim. Rerun your transaction.
除了Read Uncommitted和Snapshot,其它類型的事務都可能產生死鎖。
7.2.悲觀鎖
悲觀鎖是指假設并發更新沖突會發生,所以不管沖突是否真的發生,都會使用鎖機制。
悲觀鎖會完成以下功能:鎖住讀取的記錄,防止其它事務讀取和更新這些記錄。其它事務會一直阻塞,直到這個事務結束。
悲觀鎖是在使用了數據庫的事務隔離功能的基礎上,獨享占用的資源,以此保證讀取數據一致性,避免修改丟失。
悲觀鎖可以使用Repeatable Read事務,它完全滿足悲觀鎖的要求。
7.3.樂觀鎖
樂觀鎖不會鎖住任何東西,也就是說,它不依賴數據庫的事務機制,樂觀鎖完全是應用系統層面的東西。
如果使用樂觀鎖,那么數據庫就必須加版本字段,否則就只能比較所有字段,但因為浮點類型不能比較,所以實際上沒有版本字段是不可行的。
7.4.悲觀離線鎖
悲觀離線鎖是應用程序級別的機制,它是由應用程序實現的,不是數據庫實現的。
-
死鎖
+關注
關注
0文章
25瀏覽量
8081 -
并發控制機制
+關注
關注
0文章
2瀏覽量
5641
發布評論請先 登錄
相關推薦
評論