日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

淺談Mysql共享鎖、排他鎖、悲觀鎖、樂觀鎖及其使用場景

 觀審美2 2019-04-07

Mysql共享鎖、排他鎖、悲觀鎖、樂觀鎖及其使用場景

一、相關(guān)名詞

|--表級鎖(鎖定整個表)

|--頁級鎖(鎖定一頁)

|--行級鎖(鎖定一行)

|--共享鎖(S鎖,MyISAM 叫做讀鎖)

|--排他鎖(X鎖,MyISAM 叫做寫鎖)

|--悲觀鎖(抽象性,不真實(shí)存在這個鎖)

|--樂觀鎖(抽象性,不真實(shí)存在這個鎖)

 

二、InnoDB與MyISAM

Mysql 在5.5之前默認(rèn)使用 MyISAM 存儲引擎,之后使用 InnoDB 。查看當(dāng)前存儲引擎:

show variables like '%storage_engine%';

MyISAM 操作數(shù)據(jù)都是使用的表鎖,你更新一條記錄就要鎖整個表,導(dǎo)致性能較低,并發(fā)不高。當(dāng)然同時它也不會存在死鎖問題。

而 InnoDB 與 MyISAM 的最大不同有兩點(diǎn):一是 InnoDB 支持事務(wù);二是 InnoDB 采用了行級鎖。也就是你需要修改哪行,就可以只鎖定哪行。

在 Mysql 中,行級鎖并不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql 語句操作了主鍵索引,Mysql 就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,MySQL會先鎖定該非主鍵索引,再鎖定相關(guān)的主鍵索引。

InnoDB 行鎖是通過給索引項加鎖實(shí)現(xiàn)的,如果沒有索引,InnoDB 會通過隱藏的聚簇索引來對記錄加鎖。也就是說:如果不通過索引條件檢索數(shù)據(jù),那么InnoDB將對表中所有數(shù)據(jù)加鎖,實(shí)際效果跟表鎖一樣。因為沒有了索引,找到某一條記錄就得掃描全表,要掃描全表,就得鎖定表。

 

三、共享鎖與排他鎖

1.首先說明:數(shù)據(jù)庫的增刪改操作默認(rèn)都會加排他鎖,而查詢不會加任何鎖。

|--共享鎖:對某一資源加共享鎖,自身可以讀該資源,其他人也可以讀該資源(也可以再繼續(xù)加共享鎖,即 共享鎖可多個共存),但無法修改。要想修改就必須等所有共享鎖都釋放完之后。語法為

select * from table lock in share mode

|--排他鎖:對某一資源加排他鎖,自身可以進(jìn)行增刪改查,其他人無法進(jìn)行任何操作。語法為

select * from table for update

 

2.下面援引例子說明(援自:http://blog.csdn.net/samjustin1/article/details/52210125):

這里用T1代表一個數(shù)據(jù)庫執(zhí)行請求,T2代表另一個請求,也可以理解為T1為一個線程,T2 為另一個線程。

 

例1:-------------------------------------------------------------------------------------------------------------------------------------

T1:select * from table lock in share mode(假設(shè)查詢會花很長時間,下面的例子也都這么假設(shè))

T2:update table set column1='hello'

 

過程:

T1運(yùn)行(并加共享鎖)

T2運(yùn)行

If T1還沒執(zhí)行完

T2等......

else 鎖被釋放

T2執(zhí)行

end if

 

T2 之所以要等,是因為 T2 在執(zhí)行 update 前,試圖對 table 表加一個排他鎖,而數(shù)據(jù)庫規(guī)定同一資源上不能同時共存共享鎖和排他鎖。所以 T2 必須等 T1 執(zhí)行完,釋放了共享鎖,才能加上排他鎖,然后才能開始執(zhí)行 update 語句。

 

例2:-------------------------------------------------------------------------------------------------------------------------------------

T1:select * from table lock in share mode

T2:select * from table lock in share mode

 

這里T2不用等待T1執(zhí)行完,而是可以馬上執(zhí)行。

 

分析:

T1運(yùn)行,則 table 被加鎖,比如叫l(wèi)ockA,T2運(yùn)行,再對 table 加一個共享鎖,比如叫l(wèi)ockB,兩個鎖是可以同時存在于同一資源上的(比如同一個表上)。這被稱為共享鎖與共享鎖兼容。這意味著共享鎖不阻止其它人同時讀資源,但阻止其它人修改資源。

 

例3:-------------------------------------------------------------------------------------------------------------------------------------

T1:select * from table lock in share mode

T2:select * from table lock in share mode

T3:update table set column1='hello'

 

T2 不用等 T1 運(yùn)行完就能運(yùn)行,T3 卻要等 T1 和 T2 都運(yùn)行完才能運(yùn)行。因為 T3 必須等 T1 和 T2 的共享鎖全部釋放才能進(jìn)行加排他鎖然后執(zhí)行 update 操作。

 

例4:(死鎖的發(fā)生)-----------------------------------------------------------------------------------------------------------------

T1:begin tran

     select * from table lock in share mode

     update table set column1='hello'

T2:begin tran

     select * from table lock in share mode

     update table set column1='world'

 

假設(shè) T1 和 T2 同時達(dá)到 select,T1 對 table 加共享鎖,T2 也對 table 加共享鎖,當(dāng) T1 的 select 執(zhí)行完,準(zhǔn)備執(zhí)行 update 時,根據(jù)鎖機(jī)制,T1 的共享鎖需要升級到排他鎖才能執(zhí)行接下來的 update。在升級排他鎖前,必須等 table 上的其它共享鎖(T2)釋放,同理,T2 也在等 T1 的共享鎖釋放。于是死鎖產(chǎn)生了。

 

例5:-------------------------------------------------------------------------------------------------------------------------------------

T1:begin tran

     update table set column1='hello' where id=10

T2:begin tran

     update table set column1='world' where id=20

 

這種語句雖然最為常見,很多人覺得它有機(jī)會產(chǎn)生死鎖,但實(shí)際上要看情況

|--如果id是主鍵(默認(rèn)有主鍵索引),那么T1會一下子找到該條記錄(id=10的記錄),然后對該條記錄加排他鎖,T2,同樣,一下子通過索引定位到記錄,然后對id=20的記錄加排他鎖,這樣T1和T2各更新各的,互不影響。T2也不需要等。

|--如果id是普通的一列,沒有索引。那么當(dāng)T1對id=10這一行加排他鎖后,T2為了找到id=20,需要對全表掃描。但因為T1已經(jīng)為一條記錄加了排他鎖,導(dǎo)致T2的全表掃描進(jìn)行不下去(其實(shí)是因為T1加了排他鎖,數(shù)據(jù)庫默認(rèn)會為該表加意向鎖,T2要掃描全表,就得等該意向鎖釋放,也就是T1執(zhí)行完成),就導(dǎo)致T2等待。

 

死鎖怎么解決呢?一種辦法是,如下:

例6:-------------------------------------------------------------------------------------------------------------------------------------

T1:begin tran

     select * from table for update

     update table set column1='hello'

T2:begin tran

     select * from table for update

     update table set column1='world'

 

這樣,當(dāng) T1 的 select 執(zhí)行時,直接對表加上了排他鎖,T2 在執(zhí)行 select 時,就需要等 T1 事物完全執(zhí)行完才能執(zhí)行。排除了死鎖發(fā)生。但當(dāng)?shù)谌齻€ user 過來想執(zhí)行一個查詢語句時,也因為排他鎖的存在而不得不等待,第四個、第五個 user 也會因此而等待。在大并發(fā)情況下,讓大家等待顯得性能就太友好了。

所以,有些數(shù)據(jù)庫這里引入了更新鎖(如Mssql,注意:Mysql不存在更新鎖)。

 

例7:-------------------------------------------------------------------------------------------------------------------------------------

T1:begin tran

     select * from table [加更新鎖操作]

     update table set column1='hello'

T2:begin tran

     select * from table [加更新鎖操作]

     update table set column1='world'

 

更新鎖其實(shí)就可以看成排他鎖的一種變形,只是它也允許其他人讀(并且還允許加共享鎖)。但不允許其他操作,除非我釋放了更新鎖。T1 執(zhí)行 select,加更新鎖。T2 運(yùn)行,準(zhǔn)備加更新鎖,但發(fā)現(xiàn)已經(jīng)有一個更新鎖在那兒了,只好等。當(dāng)后來有 user3、user4...需要查詢 table 表中的數(shù)據(jù)時,并不會因為 T1 的 select 在執(zhí)行就被阻塞,照樣能查詢,相比起例6,這提高了效率。

 

后面還有意向鎖和計劃鎖:

  • 計劃鎖,和程序員關(guān)系不大,就沒去了解。
  • 意向鎖(innodb特有)分意向共享鎖和意向排他鎖。
    • 意向共享鎖:表示事務(wù)獲取行共享鎖時,必須先得獲取該表的意向共享鎖;
    • 意向排他鎖:表示事務(wù)獲取行排他鎖時,必須先得獲取該表的意向排他鎖;

我們知道,如果要對整個表加鎖,需保證該表內(nèi)目前不存在任何鎖。

因此,如果需要對整個表加鎖,那么就可以根據(jù):檢查意向鎖是否被占用,來知道表內(nèi)目前是否存在共享鎖或排他鎖了。而不需要再一行行地去檢查每一行是否被加鎖。

 

四、樂觀鎖與悲觀鎖

首先說明,樂觀鎖和悲觀鎖都是針對讀(select)來說的。

案例:

某商品,用戶購買后庫存數(shù)應(yīng)-1,而某兩個或多個用戶同時購買,此時三個執(zhí)行程序均同時讀得庫存為“n”,之后進(jìn)行了一些操作,最后將均執(zhí)行update table set 庫存數(shù)=n-1,那么,很顯然這是錯誤的。

 

解決:

  1. 使用悲觀鎖(其實(shí)說白了也就是排他鎖)

    |-- 程序A在查詢庫存數(shù)時使用排他鎖(select * from table where id=10 for update)

    |-- 然后進(jìn)行后續(xù)的操作,包括更新庫存數(shù),最后提交事務(wù)。

    |-- 程序B在查詢庫存數(shù)時,如果A還未釋放排他鎖,它將等待……

    |-- 程序C同B……
  2. 使用樂觀鎖(靠表設(shè)計和代碼來實(shí)現(xiàn))

    |-- 一般是在該商品表添加version版本字段或者timestamp時間戳字段

    |-- 程序A查詢后,執(zhí)行更新變成了:
        update table set num=num-1 where id=10 and version=23  

    這樣,保證了修改的數(shù)據(jù)是和它查詢出來的數(shù)據(jù)是一致的(其他執(zhí)行程序肯定未進(jìn)行修改)。當(dāng)然,如果更新失敗,表示在更新操作之前,有其他執(zhí)行程序已經(jīng)更新了該庫存數(shù),那么就可以嘗試重試來保證更新成功。為了盡可能避免更新失敗,可以合理調(diào)整重試次數(shù)(阿里巴巴開發(fā)手冊規(guī)定重試次數(shù)不低于三次)。

總結(jié):對于以上,可以看得出來樂觀鎖和悲觀鎖的區(qū)別:

  • 悲觀鎖實(shí)際使用了排他鎖來實(shí)現(xiàn)(select **** for update)。文章開頭說到,innodb加行鎖的前提是:必須是通過索引條件來檢索數(shù)據(jù),否則會切換為表鎖。

    因此,悲觀鎖在未通過索引條件檢索數(shù)據(jù)時,會鎖定整張表。導(dǎo)致其他程序不允許“加鎖的查詢操作”,影響吞吐。故如果在查詢居多的情況下,推薦使用樂觀鎖。

    加鎖的查詢操作”:加過排他鎖的數(shù)據(jù)行在其他事務(wù)中是不能修改的,也不能通過for updatelock in share mode的加鎖方式查詢,但可以直接通過select ...from...查詢數(shù)據(jù),因為普通查詢沒有任何鎖機(jī)制。
  • 樂觀鎖更新有可能會失敗,甚至是更新幾次都失敗,這是有風(fēng)險的。所以如果寫入居多,對吞吐要求不高,可使用悲觀鎖。

也就是一句話:讀用樂觀鎖,寫用悲觀鎖。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多