日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql记录锁与互斥锁区别_MySQL的各种锁认知

發布時間:2024/9/30 数据库 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql记录锁与互斥锁区别_MySQL的各种锁认知 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、相關名詞

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

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

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

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

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

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

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

二、InnoDB與MyISAM

Mysql 在5.5之前默認使用 MyISAM 存儲引擎,之后使用 InnoDB 。查看當前存儲引擎:

show variables like '%storage_engine%';

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

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

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

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

三、共享鎖與排他鎖

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

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

select * from table lock in share mode

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

select * from table for update --增刪改自動加了排他鎖

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

這里用T1代表一個數據庫執行請求,T2代表另一個請求,也可以理解為T1為一個線程,T2 為另一個線程。

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

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

T2:update table set column1='hello'

過程:

T1運行(并加共享鎖)

T2運行

If T1還沒執行完

T2等......

else鎖被釋放

T2執行

endif

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

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

T1:select * from table lock in share mode

T2:select * from table lock in share mode

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

分析:

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

例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 運行完就能運行,T3 卻要等 T1 和 T2 都運行完才能運行。因為 T3 必須等 T1 和 T2 的共享鎖全部釋放才能進行加排他鎖然后執行 update 操作。

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

T1:begin transelect * from table lock in share modeupdate table set column1='hello'

T2:begin transelect * from table lock in share modeupdate table set column1='world'

假設 T1 和 T2 同時達到 select,T1 對 table 加共享鎖,T2 也對 table 加共享鎖,當 T1 的 select 執行完,準備執行 update 時,根據鎖機制,T1 的共享鎖需要升級到排他鎖才能執行接下來的 update.在升級排他鎖前,必須等 table 上的其它共享鎖(T2)釋放,同理,T2 也在等 T1 的共享鎖釋放。于是死鎖產生了。

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

T1:begin tranupdate table set column1='hello' where id=10

T2:begin tranupdate table set column1='world' where id=20

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

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

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

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

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

T1:begin transelect * from table for updateupdate table set column1='hello'

T2:begin transelect * from table for updateupdate table set column1='world'

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

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

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

T1:begin transelect * from table (加更新鎖)update table set column1='hello'

T2:begin transelect * from table (加更新鎖)update table set column1='world'

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

后面還有意向鎖和計劃鎖:意向鎖即是:某行修改時,自動加上了排他鎖,同時會默認給該表加意向鎖,表示里面有記錄正被鎖定,這時,其他人就不可以對該表加表鎖了。如果沒有意向鎖這個類似指示燈的東西存在,其他人加表鎖之前就得掃描全表,查看是否有記錄正被鎖定,效率低下。而計劃鎖這些,和程序員關系不大,就沒去了解了。

悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似于write_condition機制的其實都是提供的樂觀鎖。

兩種鎖各有優缺點,不可認為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。

擴展:

本質上,數據庫的樂觀鎖做法和悲觀鎖做法主要就是解決下面假設的場景,避免丟失更新問題: 一個比較清楚的場景 下面這個假設的實際場景可以比較清楚的幫助我們理解這個問題: 假設當當網上用戶下單買了本書,這時數據庫中有條訂單號為001的訂單,其中有個status字段是’有效’,表示該訂單是有效的; 后臺管理人員查詢到這條001的訂單,并且看到狀態是有效的 用戶發現下單的時候下錯了,于是撤銷訂單,假設運行這樣一條SQL: update order_table set status = ‘取消’ where order_id = 001; 后臺管理人員由于在b這步看到狀態有效的,這時,雖然用戶在c這步已經撤銷了訂單,可是管理人員并未刷新界面,看到的訂單狀態還是有效的,于是點擊”發貨”按鈕,將該訂單發到物流部門,同時運行類似如下SQL,將訂單狀態改成已發貨:update order_table set status = ‘已發貨’ where order_id = 001

觀點1:只有沖突非常嚴重的系統才需要悲觀鎖;“所有悲觀鎖的做法都適合于狀態被修改的概率比較高的情況,具體是否合適則需要根據實際情況判斷。”,表達的也是這個意思,不過說法不夠準確;的確,之所以用悲觀鎖就是因為兩個用戶更新同一條數據的概率高,也就是沖突比較嚴重的情況下,所以才用悲觀鎖。

觀點2:最后提交前作一次select for update檢查,然后再提交update也是一種樂觀鎖的做法,的確,這符合傳統樂觀鎖的做法,就是到最后再去檢查。但是wiki在解釋悲觀鎖的做法的時候,’It is not appropriate for use in web application development.’, 現在已經很少有悲觀鎖的做法了,所以我自己將這種二次檢查的做法也歸為悲觀鎖的變種,因為這在所有樂觀鎖里面,做法和悲觀鎖是最接近的,都是先select for update,然后update

在實際應用中我們在更新數據的時候,更嚴謹的做法是帶上更新前的“狀態”,如

update order_table set status = ‘取消’ where order_id = 001 and status = ‘待支付’ and ..........;

update order_table set status = ‘已發貨’ where order_id = 001 and status = ‘已支付’ and ..........;

然后在業務邏輯代碼里判斷更新的記錄數,為0說明數據庫已經被更新了,否則是正常的。

行鎖,表鎖

InnoDB存儲引擎中有行鎖以及表鎖,行鎖是InnoDB中默認的鎖。

表鎖:對整張表進行加鎖,在同一時刻整張表的所有記錄都被鎖住。

行鎖:只對表中的某一行記錄進行加鎖,表的其余行不會被占用,但是可能會出現死鎖。

關閉事務自動提交

查看一下表數據

接著我們更新一條數據

執行成功之后我們并沒有提交事務,這個時候這一條記錄已經是加了鎖的,所以我們在另外一個客戶端更新同樣的行記錄。

自然就報錯了,直接就等待超時了。這里證明已經加鎖了,接著我們來證明是行鎖還是表鎖。

當我們執行update的時候,是update 字段a=1的 所以我們在update字段a=2的時候,雖然沒有提交事務但是還是可以執行的,這里證明了InnoDB是行鎖的。

注意:行鎖必須有索引才能實現,否則就會自動鎖住全表,也就是表鎖,而InnoDB當有主鍵的時候,自動就會創建主鍵索引。

行鎖與表鎖的區別

行鎖

優點 :粒度小, 因為加鎖的只是一行數據。

缺點 :獲取、釋放所需要做的工作更多,并且容易發生死鎖。

鎖的優化:

合理設計索引

減少基于范圍的數據檢索過濾條件

盡量控制事務的大小,盡量使用較低的事務隔離級別

盡可能讓所有的數據檢索都通過索引來完成。

表鎖

優點:獲取跟釋放快,能避免死鎖(當執行update語句的時候,把整個表鎖住了,其他的sql無法執行,所以不會造成死鎖)

缺點:粒度太大,并發不夠高,當并發量較多的時候,鎖表會讓進程無法繼續執行sql。

死鎖

死鎖出現在行鎖中,假設現在有一個T1的session線程去update一個數據庫表table1 ,而且有一個T2的session線程去update一個數據庫表table2。

在沒有提交事務的時候,table1跟table2都已經進行了加鎖,這個時候,T1去操作了table2,那么這個時候因為table2的記錄加了鎖,那么T1會一直在等待,接著T2又同樣的去操作table1的表記錄,也同樣在等待,就造成了死鎖。

InnoDB 支持多粒度鎖(multiple granularity locking),它允許行級鎖與表級鎖共存,而意向鎖就是其中的一種表鎖。

意向鎖(Intention Locks)

需要強調一下,意向鎖是一種不與行級鎖沖突的表級鎖,這一點非常重要。意向鎖分為兩種:

意向共享鎖 (intention shared lock, IS):事務有意向對表中的某些行加 共享鎖 (S鎖) -- 事務要獲取某些行的 S 鎖,必須先獲得表的 IS 鎖。 SELECT column FROM table ... LOCK IN SHARE MODE;

意向排他鎖 (intention exclusive lock, IX):事務有意向對表中的某些行加 排他鎖 (X鎖) -- 事務要獲取某些行的 X 鎖,必須先獲得表的 IX 鎖。 SELECT column FROM table ... FOR UPDATE;

即:意向鎖是有數據引擎自己維護的,用戶無法手動操作意向鎖,在為數據行加共享 / 排他鎖之前,InooDB 會先獲取該數據行所在在數據表的對應意向鎖。

意向鎖要解決的問題

我們先來看一下百度百科上對意向鎖存在意義的描述:

如果另一個任務試圖在該表級別上應用共享或排它鎖,則受到由第一個任務控制的表級別意向鎖的阻塞。第二個任務在鎖定該表前不必檢查各個頁或行鎖,而只需檢查表上的意向鎖。

設想這樣一張 users 表:MySql,InnoDB,Repeatable-Read:users(id PK,name)

事務 A 獲取了某一行的排他鎖,并未提交:

SELECT * FROM users WHERE id = 6 FOR UPDATE;

事務 B 想要獲取 users 表的表鎖:

LOCK TABLES users READ;

因為共享鎖與排他鎖互斥,所以事務 B 在視圖對 users 表加共享鎖的時候,必須保證:

當前沒有其他事務持有 users 表的排他鎖。

當前沒有其他事務持有 users 表中任意一行的排他鎖 。

為了檢測是否滿足第二個條件,事務 B 必須在確保 users表不存在任何排他鎖的前提下,去檢測表中的每一行是否存在排他鎖。很明顯這是一個效率很差的做法,但是有了意向鎖之后,情況就不一樣了:

意向鎖的兼容互斥性

意向鎖是怎么解決這個問題的呢?首先,我們需要知道意向鎖之間的兼容互斥性:

即意向鎖之間是互相兼容的,emmm......那你存在的意義是啥?

雖然意向鎖和自家兄弟互相兼容,但是它會與普通的排他 / 共享鎖互斥:

注意:這里的排他 / 共享鎖指的都是表鎖!!!意向鎖不會與行級的共享 / 排他鎖互斥!!!

現在我們回到剛才 users 表的例子:

事務 A 獲取了某一行的排他鎖,并未提交:

SELECT * FROM users WHERE id = 6 FOR UPDATE;

此時 users 表存在兩把鎖:users 表上的意向排他鎖與 id 為 6 的數據行上的排他鎖。

事務 B 想要獲取 users 表的共享鎖:

LOCK TABLES users READ;

此時事務 B 檢測事務 A 持有 users 表的意向排他鎖,就可以得知事務 A 必然持有該表中某些數據行的排他鎖,那么事務 B 對 users 表的加鎖請求就會被排斥(阻塞),而無需去檢測表中的每一行數據是否存在排他鎖。

意向鎖的并發性

這就牽扯到我前面多次強調的一件事情:

意向鎖不會與行級的共享 / 排他鎖互斥!!!意向鎖不會與行級的共享 / 排他鎖互斥!!!意向鎖不會與行級的共享 / 排他鎖互斥!!!

重要的話要加粗說三遍,正因為如此,意向鎖并不會影響到多個事務對不同數據行加排他鎖時的并發性(不然我們直接用普通的表鎖就行了)。

最后我們擴展一下上面 users 表的例子來概括一下意向鎖的作用(一條數據從被鎖定到被釋放的過程中,可能存在多種不同鎖,但是這里我們只著重表現意向鎖):

事務 A 先獲取了某一行的排他鎖,并未提交:

SELECT * FROM users WHERE id = 6 FOR UPDATE;

事務 A 獲取了 users 表上的意向排他鎖。

事務 A 獲取了 id 為 6 的數據行上的排他鎖。

之后事務 B 想要獲取 users 表的共享鎖:

LOCK TABLES users READ;

事務 B 檢測到事務 A 持有 users 表的意向排他鎖。

事務 B 對 users 表的加鎖請求被阻塞(排斥)。

最后事務 C 也想獲取 users 表中某一行的排他鎖:

SELECT * FROM users WHERE id = 5 FOR UPDATE;

事務 C 申請 users 表的意向排他鎖。

事務 C 檢測到事務 A 持有 users 表的意向排他鎖。

因為意向鎖之間并不互斥,所以事務 C 獲取到了 users 表的意向排他鎖。

因為id 為 5 的數據行上不存在任何排他鎖,最終事務 C 成功獲取到了該數據行上的排他鎖。

總結

InnoDB 支持多粒度鎖,特定場景下,行級鎖可以與表級鎖共存。

意向鎖之間互不排斥,但除了 IS 與 S 兼容外,意向鎖會與 共享鎖 / 排他鎖 互斥。

IX,IS是表級鎖,不會和行級的X,S鎖發生沖突。只會和表級的X,S發生沖突。

意向鎖在保證并發性的前提下,實現了行鎖和表鎖共存且滿足事務隔離性的要求。

總結

以上是生活随笔為你收集整理的mysql记录锁与互斥锁区别_MySQL的各种锁认知的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。