鎖:我們知道,最常用的處理多使用者并發通路的方法是加鎖。當一個使用者鎖住資料庫中的某個對象時,其他使用者就不能再通路該對象。加鎖對并發通路的影響展現在鎖的粒度上。比如,放在一個表上的鎖限制對整個表的并發通路;放在資料頁上的鎖限制了對整個資料頁的通路;放在行上的鎖隻限制對該行的并發通路。可見行鎖粒度最小,并發通路最好,頁鎖粒度最大,表鎖介于2者之間。
鎖有兩種:悲觀鎖和樂觀鎖。悲觀鎖假定其他使用者企圖通路或者改變你正在通路、更改的對象的機率是很高的,是以在悲觀鎖的環境中,在你開始改變此對象之前就将該對象鎖住,并且直到你送出了所作的更改之後才釋放鎖。悲觀的缺陷是不論是頁鎖還是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其他使用者的通路,也就是說悲觀鎖的并發通路性不好。與悲觀鎖相反,樂觀鎖則認為其他使用者企圖改變你正在更改的對象的機率是很小的,是以樂觀鎖直到你準備送出所作的更改時才将對象鎖住,當你讀取以及改變該對象時并不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖可以用較大的鎖粒度獲得較好的并發通路性能。但是如果第二個使用者恰好在第一個使用者送出更改之前讀取了該對象,那麼當他完成了自己的更改進行送出時,資料庫就會發現該對象已經變化了,這樣,第二個使用者不得不重新讀取該對象并作出更改。這說明在樂觀鎖環境中,會增加并發使用者讀取對象的次數。
從資料庫廠商的角度看,使用樂觀的頁鎖是比較好的,尤其在影響很多行的批量操作中可以放比較少的鎖,進而降低對資源的需求提高資料庫的性能。再考慮聚集索引。在資料庫中記錄是按照聚集索引的實體順序存放的。如果使用頁鎖,當兩個使用者同時通路更改位于同一資料頁上的相鄰兩行時,其中一個使用者必須等待另一個使用者釋放鎖,這會明顯地降低系統的性能。interbase和大多數關系資料庫一樣,采用的是樂觀鎖,而且讀鎖是共享的,寫鎖是排他的。可以在一個讀鎖上再放置讀鎖,但不能再放置寫鎖;你不能在寫鎖上再放置任何鎖。鎖是目前解決多使用者并發通路的有效手段。
死鎖:當二或多個工作各自具有某個資源的鎖定,但其它工作嘗試要鎖定此資源,而造成工作永久封鎖彼此時,會發生死鎖。例如:
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
waitfor delay '00:00:05'
select * from Employees
–因為Employees被連接配接2鎖住了,是以這裡會阻塞。
update Employees set LastName = LastName
commit tran
連接配接2:
--因為customers被連接配接1鎖住了,是以這裡會阻塞。
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.