事務隔離級別與樂觀鎖 悲觀鎖

2021-06-28 18:21:22 字數 3397 閱讀 2257

1.引入

在多使用者環境中,在同一時間可能會有多個使用者更新相同的記錄,這樣會產生衝突,就會造成併發性問題。

衝突型別:

(1)髒讀

指乙個事務a正在訪問資料,並且對資料進行了修改,但是這種修改還沒有提交到資料庫,這時另乙個事務b也訪問這個資料,而a事務產生了異常,發生了回滾,但是b事務使用的還是a修改後的資料,這個就是髒讀

例如:張三的工資為5000,事務a把張三的工資改為8000,但是a事務尚未提交,與此同時,事務b正在讀取張三的工資,讀取到8000,然後事務a發生了異常發生了回滾,將張三的工資又變為5000,那麼事務b讀到的張三工資為8000就是髒資料,事務b做了一次髒讀

(2)不可重複讀

指在乙個事務a中,多次讀同乙個資料,在這個事務還沒有結束時,另乙個事務b修改了同乙個資料,那麼如果在事務a中的兩次讀資料之間,事務b修改了資料,那麼事務a兩次讀取到的資料可能是不一樣的,這就是不可重複讀

例如:在事務a中,讀取到張三的工資為5000,事務還沒有提交,此時事務b將張三的工資修改為8000,並提交了事務,然後,在事務a中再次讀取張三工資,此時讀到的是8000,在同乙個事務中讀到的資料不一致,導致了不可重複讀

(3)幻讀

指在乙個事務對乙個表中的資料進行了修改,這種修改設計多條資料,此時事務b向表中插入或刪除一條新資料,那麼操作第乙個事務的使用者就會發現表中還有或者多出修改的資料行,就好像發生了幻覺一樣

例如:目前工資為5000的有10人,事務a讀取所有工資為5000的人數為10人,此時,事務b插入一條工資也為5000的記錄,這時事務a再次讀取工資為5000的員工,記錄為1000人。此時產生了幻覺

注意:髒讀和不可重複讀重點是修改:同樣的條件,讀取過的資料,再讀出來不一樣了

幻讀重點是增加或刪除:同樣的條件,第一次和第二次讀出來的記錄數不一樣了

2.事務隔離級別

(1)讀未提交

事務最低隔離級別,事務對資料只是修改了還沒提交,其他事務就可以讀出來。

髒讀、不可重複讀、幻讀都可能發生

(2)讀已提交

事務對資料的修改提交了,其他事務就可以看到。

解決了髒讀問題,但是會產生不可重複讀、幻讀的問題

(3)可重複讀

事務開始的時候,會建立乙個對資料庫對應記錄的檢視,儲存了開始時資料庫中資料的狀態,這樣即使其他事務修改了資料,本事務也讀取不到改變。

解決了髒讀、不可重複讀問題,但是仍然會產生幻讀(因為檢視只是對應行的)

(4)序列化

事務只能乙個個來,不能並行執行

3.悲觀鎖與樂觀鎖

(1)樂觀鎖

1)定義

顧名思義,就是很樂觀,我們認為系統中的事務併發更新不會很頻繁,即使衝突了也沒事,大不了重新再來一次。基本思想就是:每次提交乙個事務更新時,我們想看看修改的東西從上次讀取以後有沒有被其他事務修改過,如果修改過,那麼就會更新失敗

因為樂觀鎖不會鎖定任何記錄,所以當資料庫的事務隔離級別設定為讀已提交或者更低時,是不能避免不可重複讀問題的,所以採用樂觀鎖的時候,系統應該容許不可重複讀問題的出現

2)實現方式

在資料庫表中新增乙個version欄位來實現。

讀取資料時,將此版本號一起讀出,之後更新時,對此版本號+1。然後將提交資料的版本號和表中記錄的版本號進行對比,如果提交的資料版本號大於資料庫表當前版本號,則說明沒有其他事務修改,允許更新,否則認為是過期資料

例子:我們有乙個account的實體類,在account中多加乙個version欄位,那麼我們jdbc中sql語句將如下寫

select a.version....from account as a where (where condition..)

update account set version = version+1.....(another field) where version =?...(another contidition)

這樣一來我們就可以通過更新結果的行數來進行判斷,如果更新結果的行數為0,那麼說明實體從載入以來已經被其他事務更改了,所以就拋出自定義的樂觀鎖異常

int rowsupdated = statement.executeupdate(sql);

if(rowsupdated= =0)

在是用jdbc的情況下,我們需要在每乙個update語句中進行版本欄位的更新以及判斷,因此如果稍不小心就會出現版本字段沒有更新的問題,相反當前的orm框架為我們做好了一切,我們僅僅需要做的就是在每個實體中增減乙個version或者是date欄位

hibernate中使用樂觀鎖:

如果我們使用hibernate作為持久層框架,那麼樂觀鎖實現非常簡單,框架會幫我們生成相應的sql語句,不僅僅減少了開發人員的負擔,而且也不會出錯。還是以剛才的account實體為例:

public class account
這樣一來,每次我們提交事務時,hibernate內部會生成相應的sql語句將版本欄位+1,並進行相應的版本檢測,如果檢測到發生了樂觀鎖異常,那麼就丟擲staleobjectstateexception

(2)悲觀鎖

1)定義

顧名思義,就是很悲觀,我們認為系統中的事務併發更新很頻繁,並且事務失敗了以後重來的開銷很大,這樣一來,我們就需要真正意義上的鎖來進行實現。基本思想就是:每次乙個事務讀取資料後,就會將這些資料鎖住,這樣其他的事務要想更新,必須等以前的事務提交或者回滾解除鎖

如果我們的資料庫的事務隔離級別設定為讀已提交或者更低,那麼通過悲觀鎖,我們控制了不可重複讀問題,但是不能解決幻讀問題,因為要想避免我們就需要把資料庫的事務隔離級別設定為序列化。而一般情況下我們都會採取讀已提交或者更低的隔離級別,並且配合樂觀鎖或者悲觀鎖來實現併發控制,所以幻讀問題不能解決,如果要想避免幻讀問題,那麼只能設定資料庫的事務隔離級別為序列化

2)實現方式

悲觀鎖的實現,只能依靠資料庫提供的鎖機制。因為只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,

即使在本系統實現了加鎖機制,也無法保證外部系統不會修改資料

在jdbc中使用悲觀鎖,需要使用select  for update語句,假如我們有乙個account類,我們可以採用如下方式進行查詢:

select * from account where ...(where condition).. for update.
當使用了for update之後,每次在讀取一條記錄的時候,都會鎖住被載入的記錄,那麼當其他事務如果要更新或者是載入此記錄就會因為不能獲得鎖而阻塞,這樣避免了不可重複讀和髒讀的問題,但是其他事務還是可以插入和刪除記錄,所以會產生幻讀,但這不是悲觀鎖的問題,是我們資料庫事務隔離級別所造成的問題。

在每乙個事務中,我們必須使用select for update語句來進行資料庫操作,如果有一些事務沒有使用select for update,那麼就會很容易造成錯誤。

事務屬性 隔離級別 樂觀鎖 悲觀鎖

事務是為解決應用程式並行問題產生的,並不是資料庫天生的。事務執行要麼全部成功,要麼全部失敗。類似與二進位制,並不是0就是1。事務執行前後應用程式狀態一致。一致性通常由上層來實現,如正常執行的邏輯關聯 a發生支付動作,餘額就會減少,商戶餘額就會增加 或者回滾中的反向操作。不同事務執行互不影響 事務執行...

Redis 事務(悲觀鎖 樂觀鎖)

1 定義 redis事務是乙個單獨的隔離操作 事務中所有的命令都會被序列化 按照順序執行 事務在執行過程中不會被其他客戶端傳送來的命令請求打斷 2 作用 串聯多個命令防止別的命令插隊 multi 輸入開始命令 exec 執行命令 discard 放棄組隊 刪除掉 3 注意事項 1 multi 命令不...

悲觀鎖與樂觀鎖

悲觀鎖與樂觀鎖 悲觀鎖 pessimistic locking 顧名思義就是採用一種悲觀的態度來對待事務併發問題,我們認為系統中的併發更新會非常頻繁,並且事務失敗 了以後重來的開銷很大,這樣以來,我們就需要採用真正意義上的鎖來進行實現。悲觀鎖的基本思想就是每次一 個事務讀取某一條記錄後,就會把這條記...