2016-12-06 37 views
1

我對Oracle中鎖定的主題感到困惑。據我的研究,我可以使用FOR UPDATE NOWAIT/WAIT來鎖定行。在Oracle中實現樂觀鎖定

我想通過這種方式實現我的鎖定。一旦我發出FOR UPDATE,該行就會被鎖定,我可以檢查突變。每次更新表時,我都有一個版本號列,每次增加1。我可以使用此versionNumber來驗證該行是否已經發生了變異?像

if (:new.versionNum != :old.versionNum) raise_application_error(20000, 'Mutated'); end if;

我的問題是我去實際編寫代碼FOR UPDATE線在哪裏?我製作了一個小GUI來處理更改的名字並將它們保存回數據庫。這是在oracle上的一個觸發器上完成的,還是在我的JDBC客戶端上?

任何澄清會很好!

謝謝

+0

SELECT FOR UPDATE,但我不認爲這是一個樂觀的鎖定... – Vadim

+0

真的嗎?你能詳細說明選擇更新嗎?我認爲這鎖定了行? – wonderBoy322

+0

@ wonderBoy322它鎖定了行,但我會考慮悲觀鎖定。這是「悲觀的」,因爲當你確信別人會嘗試修改相同的行時使用它。相對於「樂觀」,並假設沒有其他人會修改同一行,所以你做了所有的工作,然後在最後檢查某種標誌以確保其他人沒有改變任何東西。 –

回答

1

有兩種常用的鎖定方法。

首先,你有悲觀鎖定。在這種方法中,您鎖定了阻止其他人更改該行的行(SELECT ... FOR UPDATE)。然後你做UPDATE。當您提交更改時,鎖定被釋放。在這種情況下,不需要有版本號/時間戳列(至少不支持鎖定),代碼相對容易。

悲觀鎖定的不利之處在於,您需要鎖定用戶在可能編輯數據的頁面上的整個時間。由於HTTP是無狀態協議,因此如果您要構建基於Web的應用程序,這在技術上確實很難。最初呈現頁面的請求通常會從連接池中獲取連接,請執行SELECT,然後在頁面完成後將連接返回到池。後續更新數據的請求通常會發生在與另一個數據庫會話不同的連接上,因此您無法在第一個會話中鎖定該行並在第二個會話中進行更新。如果您想悲觀地鎖定該行,則需要在後端執行大量工作,以確保一個數據庫連接與特定中間層會話綁定,直到用戶完成編輯數據爲止。這通常會對可伸縮性產生很大的負面影響,並且會引入各種會話管理問題 - 例如,您如何知道我是否請求了一個頁面,鎖定了一行,然後在沒有註銷或更改的情況下關閉了瀏覽器?你將離開鎖定在數據庫中的記錄多久?如果其他會話嘗試鎖定該行,會發生什麼情況?如果第一個人出去吃午飯,你會讓這個會議等待鎖定多久?通常,人們不會在基於Web的應用程序中實施悲觀鎖定,因爲管理會話和會話狀態太不切實際了。

第二個選項是樂觀鎖定。在這種方法中,您可以向該行添加版本號/時間戳。您在查詢數據時選擇此版本號/時間戳。然後,當您稍後進行更新並檢查有多少行被實際修改時,您可以在WHERE子句中使用它。如果您只修改一行,您知道自從您閱讀該行後該行沒有更改。如果您修改0行,您知道該行確實發生了變化,您可以處理該錯誤。

因此,舉例來說,你會選擇數據與版本號一起

SELECT address_line1, city, state, zip, version 
    FROM addressTable 
WHERE address_id = `<<some key>>` 

當你準備做更新,你會在那裏你在使用version做這樣的事你UPDATE如果該行改變

UPDATE addressTable 
    SET address_line1 = `<<new address line 1>>`, 
     city = `<<new city>>`, 
     state = `<<new state>>`, 
     zip = `<<new zip>>`, 
     version = version + 1 
WHERE address_id = `<<some key>>` 
    AND version = `<<version you read initially>>` 

IF(SQL%ROWCOUNT = 0) 
THEN 
    -- Darn. The row must have changed since you read it. Do something to 
    -- alert the user. Most likely, the application will need to re-query the 
    -- data to see what the address has been changed to and then ask the user 
    -- whether they want to re-apply the changes. 
    RAISE_APPLICATION_ERROR(-20001, 'Oops, the row has changed since you read it.'); 
END; 

您的應用程序,然後會做一些與錯誤有用拋出一個錯誤。通常,這意味着再次查詢數據,向用戶展示更改,並詢問他們是否仍然想要應用其更改。例如,如果我讀取地址並開始編輯它,請去午餐,我的同事登錄,讀取相同的地址,進行一些編輯並保存,然後返回並嘗試保存我的更改,這通常是有意義的告訴我,我的同事已經將地址更改爲新的東西 - 我想繼續進行編輯還是要放棄它們?

+0

第二種方法是我試圖實現,但只是有麻煩。第一個例子,你只需在SQL提示符中輸入。但在第二個例子中。這也是在SQL提示控制檯中完成的嗎?或者這是在'addressTable'上寫的觸發器中完成的? – wonderBoy322

+0

@ wonderBoy322你不會使用觸發器。我發佈的代碼可能是存儲過程的一部分。或者您可以檢查通過JDBC調用更新的行數,並將IF語句放入Java中。 –