oracle的樂觀鎖和悲觀鎖

1、問題引出html

1. 假設噹噹網上用戶下單買了本書,這時數據庫中有條訂單號爲001的訂單,其中有個status字段是’有效’,表示該訂單是有效的;java

2. 後臺管理人員查詢到這條001的訂單,而且看到狀態是有效的;數據庫

3. 用戶發現下單的時候下錯了,因而撤銷訂單,假設運行這樣一條SQL: update order_table set status = ‘取消’ where order_id = 001;安全

4. 後臺管理人員因爲在②這步看到狀態有效的,這時,雖然用戶在③這步已經撤銷了訂單,但是管理人員並未刷新界面,看到的訂單狀態仍是有效的,因而點擊」發貨」按鈕,將該訂單發到物流部門,同時運行相似以下SQL,將訂單狀態改爲已發貨:update order_table set status = ‘已發貨’ where order_id = 001;session

<<< 上面的問題應該如何解決呢??? 請見下文分解 >>>併發

 

 

2、悲觀鎖oracle

所謂的悲觀鎖:顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,因此每次拿數據的時候都會上鎖。這樣別人拿數據的時候就要等待直到鎖的釋放。性能

 

Oracle的悲觀鎖須要利用一條現有的Connection,它分紅兩種方式,從SQL語句的區別來看,就是一種是select for update,一種是select for update nowait的形式。spa

1. 執行select xxx for update操做時,數據會被鎖定,只有執行comit或rollover纔會釋放.net

2. 執行select xxx for update nowait操做時,數據也會被鎖定,其餘人訪問時或返回ORA-00054錯誤,內容是資源正忙,須要採起相應的業務措施進行處理。

 

 

雖然悲觀鎖應用起來很簡單而且十分安全,與此同時卻有兩大問題:

1. 鎖定:應用的使用者選擇一個記錄進行更新,而後去吃午餐,可是沒有結束或者丟棄該事務。這樣其餘全部須要更新該記錄的用戶就必須等待正在進行實務操做的用戶回來而且完成該事務或者直到DBA殺掉該不愉快的事務而且釋放鎖。

2. 死鎖:用戶A和B同時更新數據庫。用戶A鎖定了一條記錄而且試圖請求用戶B持有的鎖,同時用戶B也在等待獲取用戶A持有的鎖。兩個事務同時進入了無限等待狀態即進入死鎖狀態。

 

3、樂觀鎖

 

所謂的樂觀鎖:就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據。oracle默認使用樂觀鎖

 

在樂觀鎖中,咱們有3種經常使用的作法來實現:

1. 第一種就是在數據取得的時候把整個數據都copy到應用中,在進行提交的時候比對當前數據庫中的數據和開始的時候更新前取得的數據。當發現兩個數據如出一轍之後,就表示沒有衝突能夠提交,不然則是併發衝突,須要去用業務邏輯進行解決。

 

2. 第二種樂觀鎖的作法就是採用版本戳,這個在Hibernate中獲得了使用。採用版本戳的話,首先須要在你有樂觀鎖的數據庫table上創建一個新的column,好比爲number型,當你數據每更新一次的時候,版本數就會往上增長1。好比一樣有2個session一樣對某條數據進行操做。二者都取到當前的數據的版本號爲1,當第一個session進行數據更新後,在提交的時候查看到當前數據的版本還爲1,和本身一開始取到的版本相同。就正式提交,而後把版本號增長1,這個時候當前數據的版本爲2。當第二個session也更新了數據提交的時候,發現數據庫中版本爲2,和一開始這個session取到的版本號不一致,就知作別人更新過此條數據,這個時候再進行業務處理,好比整個Transaction都Rollback等等操做。在用版本戳的時候,能夠在應用程序側使用版本戳的驗證,也能夠在數據庫側採用Trigger(觸發器)來進行驗證。不過數據庫的Trigger的性能開銷仍是比較的大,因此能在應用側進行驗證的話仍是推薦不用Trigger。

 

3. 第三種作法和第二種作法有點相似,就是也新增一個Table的Column,不過此次這個column是採用timestamp型,存儲數據最後更新的時間。在Oracle9i之後能夠採用新的數據類型,也就是timestamp with time zone類型來作時間戳。這種Timestamp的數據精度在Oracle的時間類型中是最高的,精確到微秒(還沒與到納秒的級別),通常來講,加上數據庫處理時間和人的思考動做時間,微秒級別是很是很是夠了,其實只要精確到毫秒甚至秒都應該沒有什麼問題。和剛纔的版本戳相似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和本身更新前取到的時間戳進行對比,若是一致則OK,不然就是版本衝突。若是不想把代碼寫在程序中或者因爲別的緣由沒法把代碼寫在現有的程序中,也能夠把這個時間戳樂觀鎖邏輯寫在Trigger或者存儲過程當中。

 

4、結論

1. 若是系統併發量不大且不容許髒讀,可使用悲觀鎖解決併發問題。

2. 若是系統併發很是大的話,悲觀鎖會帶來很大性能問題,因此通常採用樂觀鎖。

3. 若是系統讀比較多,寫比較少,也應該使用樂觀鎖,能夠提升吞吐量。

 

5、參考資料

[1] oracle的樂觀鎖和悲觀鎖 http://www.blogjava.net/cheneyfree/archive/2008/01/25/177773.html

[2] 樂觀鎖和悲觀鎖的區別 http://www.cnblogs.com/Bob-FD/p/3352216.html

[3] Oracle併發控制中的樂觀鎖 http://blog.csdn.net/ivory_lei/article/details/7940117

相關文章
相關標籤/搜索