Oracle悲觀(guān)鎖和樂(lè)觀(guān)鎖淺析 |
發(fā)布時(shí)間: 2012/8/21 16:32:33 |
為了得到最大的性能,一般數(shù)據(jù)庫(kù)都有并發(fā)機(jī)制,不過(guò)帶來(lái)的問(wèn)題就是數(shù)據(jù)訪(fǎng)問(wèn)的沖突。為了解決這個(gè)問(wèn)題,大多數(shù)數(shù)據(jù)庫(kù)用的方法就是數(shù)據(jù)的鎖定。 數(shù)據(jù)的鎖定分為兩種方法,第一種叫做悲觀(guān)鎖,第二種叫做樂(lè)觀(guān)鎖。什么叫悲觀(guān)鎖呢,悲觀(guān)鎖顧名思義,就是對(duì)數(shù)據(jù)的沖突采取一種悲觀(guān)的態(tài)度,也就是說(shuō)假設(shè)數(shù)據(jù)肯定會(huì)沖突,所以在數(shù)據(jù)開(kāi)始讀取的時(shí)候就把數(shù)據(jù)鎖定住。而樂(lè)觀(guān)鎖就是認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則讓用戶(hù)返回錯(cuò)誤的信息,讓用戶(hù)決定如何去做。 先從悲觀(guān)鎖開(kāi)始說(shuō)。在SqlServer等其余很多數(shù)據(jù)庫(kù)中,數(shù)據(jù)的鎖定通常采用頁(yè)級(jí)鎖的方式,也就是說(shuō)對(duì)一張表內(nèi)的數(shù)據(jù)是一種串行化的更新插入機(jī)制,在任何時(shí)間同一張表只會(huì)插1條數(shù)據(jù),別的想插入的數(shù)據(jù)要等到這一條數(shù)據(jù)插完以后才能依次插入。帶來(lái)的后果就是性能的降低,在多用戶(hù)并發(fā)訪(fǎng)問(wèn)的時(shí)候,當(dāng)對(duì)一張表進(jìn)行頻繁操作時(shí),會(huì)發(fā)現(xiàn)響應(yīng)效率很低,數(shù)據(jù)庫(kù)經(jīng)常處于一種假死狀態(tài)。而Oracle用的是行級(jí)鎖,只是對(duì)想鎖定的數(shù)據(jù)才進(jìn)行鎖定,其余的數(shù)據(jù)不相干,所以在對(duì)Oracle表中并發(fā)插數(shù)據(jù)的時(shí)候,基本上不會(huì)有任何影響。 注:對(duì)于悲觀(guān)鎖是針對(duì)并發(fā)的可能性比較大,而一般在我們的應(yīng)用中用樂(lè)觀(guān)鎖足以。
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 通過(guò)這條檢索語(yǔ)句鎖定以后,再開(kāi)另外一個(gè)sql*plus窗口進(jìn)行操作,再把上面這條sql語(yǔ)句執(zhí)行一便,你會(huì)發(fā)現(xiàn)sqlplus好像死在那里了,好像檢索不到數(shù)據(jù)的樣子,但是也不返回任何結(jié)果,就屬于卡在那里的感覺(jué)。這個(gè)時(shí)候是什么原因呢,就是一開(kāi)始的第一個(gè)Session中的select for update語(yǔ)句把數(shù)據(jù)鎖定住了。由于這里鎖定的機(jī)制是wait的狀態(tài)(只要不表示nowait那就是wait),所以第二個(gè)Session(也就是卡住的那個(gè)sql*plus)中當(dāng)前這個(gè)檢索就處于等待狀態(tài)。當(dāng)?shù)谝粋(gè)session最后commit或者rollback之后,第二個(gè)session中的檢索結(jié)果就是自動(dòng)跳出來(lái),并且也把數(shù)據(jù)鎖定住。不過(guò)如果你第二個(gè)session中你的檢索語(yǔ)句如下所示。 select * from test where id = 10
select * from test where id = 10 for update nowait
update test set value=2 where id = 10
總之,Oracle中的悲觀(guān)鎖就是利用Oracle的Connection對(duì)數(shù)據(jù)進(jìn)行鎖定。在Oracle中,用這種行級(jí)鎖帶來(lái)的性能損失是很小的,只是要注意程序邏輯,不要給你一不小心搞成死鎖了就好。而且由于數(shù)據(jù)的及時(shí)鎖定,在數(shù)據(jù)提交時(shí)候就不呼出現(xiàn)沖突,可以省去很多惱人的數(shù)據(jù)沖突處理。缺點(diǎn)就是你必須要始終有一條數(shù)據(jù)庫(kù)連接,就是說(shuō)在整個(gè)鎖定到最后放開(kāi)鎖的過(guò)程中,你的數(shù)據(jù)庫(kù)聯(lián)接要始終保持住。與悲觀(guān)鎖相對(duì)的,我們有了樂(lè)觀(guān)鎖。樂(lè)觀(guān)鎖一開(kāi)始也說(shuō)了,就是一開(kāi)始假設(shè)不會(huì)造成數(shù)據(jù)沖突,在最后提交的時(shí)候再進(jìn)行數(shù)據(jù)沖突檢測(cè)。在樂(lè)觀(guān)鎖中,我們有3種 常用的做法來(lái)實(shí)現(xiàn)。 [1]第一種就是在數(shù)據(jù)取得的時(shí)候把整個(gè)數(shù)據(jù)都copy到應(yīng)用中,在進(jìn)行提交的時(shí)候比對(duì)當(dāng)前數(shù)據(jù)庫(kù)中的數(shù)據(jù)和開(kāi)始的時(shí)候更新前取得的數(shù)據(jù)。當(dāng)發(fā)現(xiàn)兩個(gè)數(shù)據(jù)一模一樣以后,就表示沒(méi)有沖突可以提交,否則則是并發(fā)沖突,需要去用業(yè)務(wù)邏輯進(jìn)行解決。 [2]第二種樂(lè)觀(guān)鎖的做法就是采用版本戳,這個(gè)在Hibernate中得到了使用。采用版本戳的話(huà),首先需要在你有樂(lè)觀(guān)鎖的數(shù)據(jù)庫(kù)table上建立一個(gè)新的column,比如為number型,當(dāng)你數(shù)據(jù)每更新一次的時(shí)候,版本數(shù)就會(huì)往上增加1。比如同樣有2個(gè)session同樣對(duì)某條數(shù)據(jù)進(jìn)行操作。兩者都取到當(dāng)前的數(shù)據(jù)的版本號(hào)為1,當(dāng)?shù)谝粋(gè)session進(jìn)行數(shù)據(jù)更新后,在提交的時(shí)候查看到當(dāng)前數(shù)據(jù)的版本還為1,和自己一開(kāi)始取到的版本相同。就正式提交,然后把版本號(hào)增加1,這個(gè)時(shí)候當(dāng)前數(shù)據(jù)的版本為2。當(dāng)?shù)诙䝼(gè)session也更新了數(shù)據(jù)提交的時(shí)候,發(fā)現(xiàn)數(shù)據(jù)庫(kù)中版本為2,和一開(kāi)始這個(gè)session取到的版本號(hào)不一致,就知道別人更新過(guò)此條數(shù)據(jù),這個(gè)時(shí)候再進(jìn)行業(yè)務(wù)處理,比如整個(gè)Transaction都Rollback等等操作。在用版本戳的時(shí)候,可以在應(yīng)用程序側(cè)使用版本戳的驗(yàn)證,也可以在數(shù)據(jù)庫(kù)側(cè)采用Trigger(觸發(fā)器)來(lái)進(jìn)行驗(yàn)證。不過(guò)數(shù)據(jù)庫(kù)的Trigger的性能開(kāi)銷(xiāo)還是比較的大,所以能在應(yīng)用側(cè)進(jìn)行驗(yàn)證的話(huà)還是推薦不用Trigger。 [3]第三種做法和第二種做法有點(diǎn)類(lèi)似,就是也新增一個(gè)Table的Column,不過(guò)這次這個(gè)column是采用timestamp型,存儲(chǔ)數(shù)據(jù)最后更新的時(shí)間。在Oracle9i以后可以采用新的數(shù)據(jù)類(lèi)型,也就是timestamp with time zone類(lèi)型來(lái)做時(shí)間戳。這種Timestamp的數(shù)據(jù)精度在Oracle的時(shí)間類(lèi)型中是最高的,精確到微秒(還沒(méi)到納秒的級(jí)別),一般來(lái)說(shuō),加上數(shù)據(jù)庫(kù)處理時(shí)間和人的思考動(dòng)作時(shí)間,微秒級(jí)別是非常非常夠了,其實(shí)只要精確到毫秒甚至秒都應(yīng)該沒(méi)有什么問(wèn)題。和剛才的版本戳類(lèi)似,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫(kù)中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比,如果一致則OK,否則就是版本沖突。如果不想把代碼寫(xiě)在程序中或者由于別的原因無(wú)法把代碼寫(xiě)在現(xiàn)有的程序中,也可以把這個(gè)時(shí)間戳樂(lè)觀(guān)鎖邏輯寫(xiě)在Trigger或者存儲(chǔ)過(guò)程中。 本文出自:億恩科技【www.allwellnessguide.com】 服務(wù)器租用/服務(wù)器托管中國(guó)五強(qiáng)!虛擬主機(jī)域名注冊(cè)頂級(jí)提供商!15年品質(zhì)保障!--億恩科技[ENKJ.COM] |