Oracle悲觀鎖和樂觀鎖淺析 |
發布時間: 2012/8/21 16:32:33 |
為了得到最大的性能,一般數據庫都有并發機制,不過帶來的問題就是數據訪問的沖突。為了解決這個問題,大多數數據庫用的方法就是數據的鎖定。 數據的鎖定分為兩種方法,第一種叫做悲觀鎖,第二種叫做樂觀鎖。什么叫悲觀鎖呢,悲觀鎖顧名思義,就是對數據的沖突采取一種悲觀的態度,也就是說假設數據肯定會沖突,所以在數據開始讀取的時候就把數據鎖定住。而樂觀鎖就是認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則讓用戶返回錯誤的信息,讓用戶決定如何去做。 先從悲觀鎖開始說。在SqlServer等其余很多數據庫中,數據的鎖定通常采用頁級鎖的方式,也就是說對一張表內的數據是一種串行化的更新插入機制,在任何時間同一張表只會插1條數據,別的想插入的數據要等到這一條數據插完以后才能依次插入。帶來的后果就是性能的降低,在多用戶并發訪問的時候,當對一張表進行頻繁操作時,會發現響應效率很低,數據庫經常處于一種假死狀態。而Oracle用的是行級鎖,只是對想鎖定的數據才進行鎖定,其余的數據不相干,所以在對Oracle表中并發插數據的時候,基本上不會有任何影響。 注:對于悲觀鎖是針對并發的可能性比較大,而一般在我們的應用中用樂觀鎖足以。
CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept
select * from test where id = 10 for update 通過這條檢索語句鎖定以后,再開另外一個sql*plus窗口進行操作,再把上面這條sql語句執行一便,你會發現sqlplus好像死在那里了,好像檢索不到數據的樣子,但是也不返回任何結果,就屬于卡在那里的感覺。這個時候是什么原因呢,就是一開始的第一個Session中的select for update語句把數據鎖定住了。由于這里鎖定的機制是wait的狀態(只要不表示nowait那就是wait),所以第二個Session(也就是卡住的那個sql*plus)中當前這個檢索就處于等待狀態。當第一個session最后commit或者rollback之后,第二個session中的檢索結果就是自動跳出來,并且也把數據鎖定住。不過如果你第二個session中你的檢索語句如下所示。 select * from test where id = 10
select * from test where id = 10 for update nowait
update test set value=2 where id = 10
總之,Oracle中的悲觀鎖就是利用Oracle的Connection對數據進行鎖定。在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或者存儲過程中。 本文出自:億恩科技【www.laynepeng.cn】 |