資料庫事務隔離級別

2021-10-07 20:42:19 字數 4880 閱讀 5966

2. 事務隔離級別

3. 資料庫鎖機制

4. 悲觀鎖與樂觀鎖

三種資料讀取問題出現的根本原因:併發訪問

讀取未提交資料

a事務讀取b事務尚未提交的資料,此時如果b事務發生錯誤並執行回滾操作,那麼a事務讀取到的資料就是髒資料。

就好像原本的資料是艾希 比較乾淨、純粹。b事務更改艾希為蓋倫,a事務在之後讀取了這個資料(蓋倫)。然後b又把資料回滾為艾希,而事務a卻什麼都不知道,傻傻的以為蓋倫就是正確的值。最終結果就是事務a讀取了此次的髒資料,稱為髒讀。

前後多次讀取,資料內容不一致

a事務在執行讀取操作,且耗時較長。事務a第一次讀取資料時,讀到庫中值為 艾希,此時事務b執行更改操作,將值改為 蓋倫 後提交。之後事務a第二次讀取到庫中值時,發現和之前的資料不一樣了(第一次讀到艾希,第二次卻成了蓋倫)。即系統不可以讀取到重複的資料了,稱為不可重複讀。

前後多次讀取,資料總量不一致

a事務在執行讀取操作,需要兩次統計庫中艾希的總數量。第一次查詢庫中艾希資料總量為1,此時事務b執行了新增100個艾希的操作並提交。事務a第二次再去讀取時發現艾希的資料總量變成了101。這和之前統計的不一樣,就像產生了幻覺一樣,按相同的查詢條件檢索卻平白無故的多了幾條資料,稱為幻讀。

隔離級別

髒讀不可重複讀

幻讀預設資料庫

read uncommitted 讀未提交××

×read committed 讀已提交√×

×oracle、sql server…

repeatable read 可重複讀√√

×mysql

serializable 可序列化√√

√mysql 在5.5之前預設使用 myisam 儲存引擎,之後使用 innodb 。

myisam :使用的是表鎖。更新一條記錄就要鎖整個表,導致效能較低,併發不高。當然同時也不會存在死鎖問題

innodb:採用了行鎖且支援事務(myisam不支援事務)

在 mysql 中,行級鎖並不是直接鎖記錄,而是鎖索引

索引分為 主鍵索引 和 非主鍵索引 兩種,如果一條sql 語句操作了主鍵索引,mysql 就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,mysql會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。

innodb 行鎖是通過給索引項加鎖實現的,如果沒有索引,innodb 會通過隱藏的聚簇索引來對記錄加鎖。也就是說:如果不通過索引條件檢索資料,那麼innodb將對錶中所有資料加鎖,實際效果跟表鎖一樣。因為沒有了索引,找到某一條記錄就得掃瞄全表,要掃瞄全表,就得鎖定表。

共享鎖:又稱 讀鎖 或 s鎖

對某一資源加共享鎖,自身可以讀該資源,其他人也可以讀該資源(可以共存多個共享鎖),但無法修改。要想修改就必須等所有共享鎖都釋放完之後。

排他鎖:又稱 寫鎖 或 x鎖

對某一資源加排他鎖,自身可以進行增刪改查,其他人無法進行任何操作。

資料庫預設:增刪改操作預設都會加排他鎖,而查詢不會加任何鎖。

注意:資料庫規定同一資源上不能同時共存共享鎖和排他鎖

t1:select * from table lock in share mode

t2:select * from table lock in share mode

t3:update table set column1=

'hello'

t2 不用等 t1 執行完就能執行,t3 卻要等 t1 和 t2 都執行完才能執行。因為 t3 必須等 t1 和 t2 的共享鎖全部釋放才能進行加排他鎖然後執行 update 操作。

t1:  select

*from

table

lock

inshare

mode

update

table

set column1=

'hello'

t2: select

*from

table

lock

inshare

mode

update

table

set column1=

'world'

假設 t1 和 t2 同時達到 select,t1 對 table 加共享鎖,t2 也對 table 加共享鎖。

當 t1 的 select 執行完,準備執行 update 時,根據鎖機制,t1 的共享鎖需要公升級到排他鎖才能執行接下來的 update。在公升級排他鎖前,必須等 table 上的其它共享鎖(t2)釋放。

同理,t2 也在等 t1 的共享鎖釋放。於是死鎖產生了。

另一種死鎖:

t1

:begin tran

update table set column1=

'hello'

where id=10;

t2:begin tran

update table set column1=

'world'

where id=20;

這種語句雖然最為常見,很多人覺得它有機會產生死鎖,但實際上要看情況

1. 如果id是主鍵(預設有主鍵索引),那麼t1會瞬間找到該條記錄(id=

10的記錄),然後對該條記錄加排他鎖。t2同理,這樣t1和t2各更新各的,互不影響。此時t2不需要等待。

2. 如果id是普通的一列,沒有索引。那麼當t1對id

=10這一行加排他鎖後,t2為了找到id

=20,需要對全表掃瞄。

但因為t1已經為一條記錄加了排他鎖,導致t2的全表掃瞄進行不下去(其實是因為t1加了排他鎖,資料庫缺省會為該錶加意向鎖,t2要掃瞄全表,就得等該意向鎖釋放,也就是t1執行完成),就導致t2等待。

3.3.2.1 直接加排他鎖
t1:	select

*from

table

(xlock)

(xlock意思是直接對錶加排他鎖)

update

table

set column1=

'hello'

t2: select

*from

table

(xlock)

update

table

set column1=

'world'

這樣,當t1的select 執行時,直接對錶加上了排他鎖,t2在執行select時,就需要等t1事物完全執行完才能執行。排除了死鎖發生。

弊端:當第三個user過來想執行乙個查詢語句時,也因為排他鎖的存在而不得不等待,第四個、第五個user也會因此而等待,會產生效能問題。

3.3.2.2 加更新鎖
更新鎖其實就可以看成排他鎖的一種變形,只是它也允許其他人讀(還允許加共享鎖)。但不允許其他操作,除非我釋放了更新鎖

t1 執行 select,加更新鎖。

t2 執行,準備加更新鎖,但發現已經有乙個更新鎖在那兒了,只好等。

當後來有 user3、user4…需要查詢 table 表中的資料時,並不會因為 t1 的 select 在執行就被阻塞,照樣能查詢,相比起上例,這提高了效率。

首先說明,悲觀鎖和樂觀鎖都是針對讀(select)來說的。只要加鎖了就屬於悲觀策略。

總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,用完了才放。

悲觀鎖適用於多寫的場景下。

總是假設最好的情況,每次去拿資料的時候都認為別人不會修改,所以不會上鎖。但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料。

樂觀鎖適用於寫比較少的情況下(多讀場景),這樣可以提高吞吐量

4.2.1 版本號機制

在資料表中加上乙個資料版本號version欄位,表示資料被修改的次數。當資料被修改時,version值會加1

當執行緒a要更新資料值時,在讀取資料的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前資料庫中的version值相等時才更新,否則重試更新操作,直到更新成功。(阿里開發手冊要求重試操作至少為3次)

賬戶餘額:100元

version :1

a操作員:讀取version =

=1;update 賬戶餘額為 50

;b操作員:讀取version =

=1;update 賬戶餘額為 150

;a操作員:完成任務,開始確認version號與開始時讀取的(

1)是否一致,發現一致,提交事務。

此時由於提交的資料被更新,資料庫記錄 version 更新為 2 。;

b操作員:完成任務,卻發現當前庫中版本號(

2)與自己開始讀取版本號(

1)不一致,操作員 b 的提交被駁回, 重試更新操作。

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable,這四個級別可以逐個解決髒讀 不可重複讀 幻讀這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted rea...

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable,這四個級別可以逐個解決髒讀 不可重複讀 幻讀這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted rea...

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable 這四個級別可以逐個解決髒讀 不可重複讀 幻讀 這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted re...