久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL中的鎖怎么理解

154次閱讀
沒有評論

共計 8258 個字符,預計需要花費 21 分鐘才能閱讀完成。

本篇內容主要講解“MySQL 中的鎖怎么理解”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“MySQL 中的鎖怎么理解”吧!

01. 怎么認識 鎖

  簡單的說,鎖 (locking) 是數據庫中的一項機制,用于處理多個事務間的協同關系

  可以把它看成是數據庫對某些記錄或數據表的一種標記,用于指示資源當前狀態是否被某些事務占用

02. 鎖 的分類

  按照加鎖思想不同,可區分樂觀鎖 (optimistic locking) 和悲觀鎖(pessimistic locking) mdash; mdash; 這是一個虛構的概念

  按照加鎖策略,可分為記錄鎖(record locking)、間隙鎖(gap locking)和臨鍵鎖(next-key locking),其中臨鍵鎖 = 記錄鎖 + 間隙鎖

  按照加鎖粒度,可分為行鎖(row-level locking)和表鎖(table-level locking),其中 InnoDB 可以加行鎖,也可以加表鎖;MyISAM 只能加表鎖

  按照加鎖影響,可區分共享鎖(share locking,S 鎖)和排他鎖(exclusive locking,X 鎖),二者又分別稱作讀鎖和寫鎖

  事務加鎖之前要先發出 請求,所以就產生了意向鎖(intention locking),相當于是向引擎發出一個加鎖的意向:又可細分為共享意向鎖(intention share locking,IS)和排他意向鎖(intention exclusive locking,IX),請求成功(請求加鎖的目標未被占用)則變成相應的 S 鎖或 X 鎖,否則便處于等待狀態或者超時退出。

03. 加 鎖 過程

  加鎖過程一般分為兩個階段,即加鎖(locking phase)和解鎖(unlocking phase),所以也叫兩階段鎖(two-phase locking)

  鎖的作用范圍是事務,所以加鎖只能在開啟事務之后由某些 SQL 語句觸發,而當提交事務或回滾時釋放鎖

04. 給誰加 鎖

  不是所有的 SQL 語句都加鎖,例如 DDL(數據定義語言)和 DCL(數據控制語言)因不涉及事務,自然不存在鎖的問題

  也不是所有的 DQL(特指數據查詢語言,形如 select hellip; hellip;)都加鎖,例如普通的 select 語句都不加鎖,而是依靠 MVCC(multi-version concurrency control,即多版本并發控制)來實現事務的 某種 一致性

  普通 select 語句不加鎖,如想加鎖只需在 select 語句后明確指定 for share 或 for update 即可,其中前者就是共享鎖(S 鎖),也叫讀鎖;后者是排他鎖(X 鎖),也叫寫鎖

  但是所有的 DML 語句(數據操作語言,insert、update 和 delete)都會自動加鎖,而且加的是排他鎖(X 鎖)

05. 加 鎖 目的

  加鎖的目的是為了數據庫的穩定性和一致性,但其副作用是降低了并發能力,所以加鎖策略往往要在一致性(consistency)和并發能力(concurrency)間折中

  加鎖是為了權衡數據一致性和并發能力,MySQL 中不加鎖實現這一機制的方法是 MVCC,即大名鼎鼎的多版本并發控制;與之對應,加鎖實現的并發機制則叫做 LBCC(locking-based concurrency control)

06. 加 鎖 對象

  表鎖,是對整個表進行鎖定,如果是虛擬的視圖(view)、觸發器(trigger),則會將其關聯的所有表進行鎖定

  行鎖,實際鎖的對象不是行,而是按索引鎖定,也就是說鎖不會定位到某條記錄,而是通過限制索引來間接作用到記錄

07. 鎖 和事務

 SQL 通用標準定義了事務的 ACID 四大屬性,即原子性 Atomcity,一致性 Consistency,隔離性 Isolation,持久性 Durability

  為了實現隔離性進而確保一致性,需要實現事務;事務的實現又依賴于存儲引擎,MySQL 的兩種常用引擎中,默認引擎 InnoDB 支持事務,而 MyISAM 則不支持

  前面提到,普通的查詢語句不加任何鎖,此時 innoDB 引擎依靠 MVCC 機制實現數據庫的隔離性和一致性。MVCC,簡單的說就是對可能存在并發和爭議的記錄增加帶有版本信息的隱藏字段,例如時間戳,來確保多次查詢數據的一致性

  一致性的狀態又具體因隔離級別不同而異,SQL92 標準(數據庫通用標準,非 MySQL 獨有)定義了四大隔離等級:

      a.  讀未提交(Read Uncommitted,RU),即一個事務可以讀到其他事務已操作但未提交的數據,當這個操作回滾時,即發生臟讀

      b.  讀已提交(Read Committed,RC),即一個事務僅能讀到其他事務已提交的數據,確保這個數據是實實在在真實的數據,避免了臟讀,但可能導致本事務窗口內前后查詢結果不一致,即不可重復讀

      c.  可重復讀(Repeatable Read,RR),即可重復讀,基于 MVCC 機制,在當前事務中的首次查詢時,記錄一個快照版本,同一事務期間的后續查詢均采用當前快照版本的結果,所以即使是其他事務已提交的數據,但若其快照版本在本事務首次快照版本之后,也不會讀出來。注意,這里當前事務采集的快照 版本號 取決于首次查詢的時機,而不是開始事務的時機。

      d.  串行化(Serializable,SE),嚴格限制并發,多個事務間在存在數據競爭時串行執行,數據穩定性和一致性最強,但并發能力受到極大限制。注意,這里是指存在數據沖突時事務間串行,否則仍可并發

  不是所有的數據庫都必須包含這 4 種隔離級別(例如 Oracle 數據庫主要支持 RC 和 SE 兩個隔離級別),不同數據庫實現的方式也不盡相同。MySQL 支持全部 4 個隔離級別,默認為 RR 級別

  默認情況下,MySQL 執行的每條 SQL 語句都是自動提交的,如果想顯式的執行事務,有兩種方法: 

1##  開啟事務 2 種方法  2--  一種是顯式開啟事務  3START TRANSACTION / BEGIN 4--  另一種是關閉自動提交  5SET autocommit = 0 6 7##  結束事務  8COMMIT / ROLLBACK

  對于未顯式開啟事務的 SQL 語句,可將其看做是在語句前后分別自動開啟和提交事務,即: 

1select  hellip; hellip;; 2 等價于  3START TRANSACTION; 4selece  hellip; hellip;; 5COMMIT;

08. 讀象

read phenomena,官方文檔給出的英文寫法,未找到相關權威翻譯名詞。特指 MySQL 讀取過程中存在的副作用,例如臟讀、幻讀等

 read phenomena,主要是指數據庫中三種 錯誤 的讀取結果:

  臟讀:dirty read,即 A 事務讀取了 B 事務更改但未提交的信息,主要發生在 RU 隔離級別

  不可重復讀,non-repeatable read,即由于 B 事務在 A 事務期間對數據更改并已提交,導致 A 事務前后讀取到不一致的結果

  幻讀,phantom read,即 A 事務在之后的查詢中出現了前期未出現的記錄。

  鑒于部分資料對幻讀和不可重復讀解釋很亂,這里再說下幻讀和不可重讀區別:

  不可重復讀,顧名思義,是指前后兩次讀取結果不一致,這里的不一致涵蓋的范圍很廣,換言之只要前后不一致就都屬于不可重復讀。造成原因主要是一項事務在執行期間,其他事務對數據表進行了更改并提交(如果未提交就能讀到那么性質更惡劣,屬于臟讀),主要發生在 RC 隔離級別,因為 RC 意味著 讀已提交,所以但凡其他事務已提交的數據更新該事務都能察覺到,前后結果當然可能不一致

  而幻讀,顧名思義,是指讀到了之前未曾發現的記錄,當然,從某種意義上將之前未曾發覺肯定也屬于不可重復讀,這樣理解本身是沒錯的,只是二者側重點不一樣。幻讀側重于在本事務執行期間,其他事務插入(insert)了新的記錄,造成本事務之后讀取到了前期不曾發現的事務,好似發生幻覺一樣,是謂幻讀。

需要指出:MySQL 依靠 MVCC 的快照機制,某種程度上 RR 隔離級別已經避免了幻讀,但仍可觸發,官方文檔也給予相應的說明。具體請閱讀后面的實戰案例。

09. 快照讀和當前讀

  快照讀,snapshot read,也叫一致讀或非加鎖讀,consistent nonlocking read,指不依靠加鎖來保證查詢數據一致性,是 MySQL 中 RR 和 RC 級別下的默認查詢語句執行方式,通過 MVCC 機制實現按 快照 版本號執行讀操作。RR 級別和 RC 級別采集 快照 原則是不同的,這也是導致兩種隔離級別存在不同 讀象(不可重讀或幻讀)的原因,其中:

  RR 級別以進入事務后第一次讀操作的時間作為快照版本(注意是第一次讀操作的時間,而與開啟事務時間無關),一旦確定快照版本,則在本事務后續讀操作中就都應用此快照結果

  RC 級別是每次讀操作時均采集快照,所以當其他事務提交后它能及時采集到新的快照

  普通查詢語句中,RC 級別因為存在臟讀,所以不屬于一致讀

 SE 級別因為是靠加鎖(默認對普通 select 語句加 S 鎖)來實現數據一致,能夠確保讀取到一致的結果,但已不是原原本本的一致讀

  當前讀,current read,也叫加鎖讀,即 locking read,特指在普通查詢語句后增加 for share 或 for update 來指定共享讀或排他讀的讀操作,其中:

  for share,即加 S 鎖,允許多個事務同時獲取該 S 鎖,是謂共享

  for update,即加 X 鎖,僅供獲取到該 X 鎖的事務操作,是謂排他

  由于加鎖讀是建立在事務的基礎上,所以必須顯式開啟事務后,加鎖讀才有意義,否則因為事務的

實戰案例篇

以下所有案例均依托 Navicat Primium12 工具。初始建表語句:

1create table test(id int, name varchar(20), primary key(id)); 2insert into test values(1,  A  3insert into test values(3,  C

10. 3 種 讀象

臟讀、不可重復讀和幻讀應該是困擾很多人的一個常見概念問題,尤其是后兩者的區別,這里通過幾個案例進行闡釋說明。

  臟讀,dirty read

首先來看官方文檔給出的定義:

An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. It is only possible with the isolation level known as read uncommitted.

大意:某個操作中處理了由其他事務更新但尚未提交的數據,這個數據是不可靠的數據,僅發生于 RU 隔離級別。

案例:

RU 存在臟讀:事務 A 讀到了事務 B 更改但未提交的數據

  不可重復讀,non-repeatable read

官方文檔給出的定義:

The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime).

大意:在一項事務查詢數據期間,由于其他事務同時進行了提交,造成其前后兩次查詢到的數據結果不一致。

案例:

RC 避免了臟讀,但存在不可重復讀

  幻讀,phantom read

A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.

大意:之前查詢的結果中不存在、但之后查詢得到的記錄稱作是幻讀。例如,一個查詢執行兩次,期間另一個事務進行了插入或更新記錄并提交,導致前一個事務兩次查詢結果不一致。

個人觀點,幻讀本身當然屬于不可重復讀的一種,畢竟兩次讀取結果 不一致。但幻讀側重的是之前沒有、之后虛幻出來了新行這種特定操作。

案例:

①,RR 級別可避免 RC 級別中的不可重復讀問題:

RR 不存在不可重復讀數據

②,特殊情況下仍可觸發幻讀

RR 級別下,特殊操作仍可觸發幻讀(更新快照)

實際上,MVCC 機制只是為保證讀取結果采取快照的方式,所以能保證可重復讀,但對于執行 insert、update 和 delete 操作時,仍然會實際檢測當前數據庫中最新的記錄狀態:當其他事務提交的最新數據與本事務中的增刪改操作符合條件時,仍然會有影響。

這點不難理解,畢竟要保證數據庫的狀態一致性,但值得詫異的是經過 update 之后,居然會更新事務中的快照版本。例如圖中所示案例,初次查詢有 2 條記錄,update 時實際更新的是 3 條,但再次查詢時結果也更新成了 3 條。而且,更重要的是,這種現象并不具有普遍性:僅當事務執行 update 操作時才會更新快照版本,而對于 delete 和 insert 操作則是只檢測狀態不更新快照版本。

事務的 insert 操作不會更新快照版本

更一般的,進一步測試了事務 B 執行的其他增刪改操作對事務 A 是否更新快照版本的影響,兩兩組合,得到如下試驗結論:

如上幻讀僅發生在其他事務插入新記錄且提交后,本事務更新數據后的再次查詢中

當然,官方文檔對此給出了注解:

大意是說:快照讀(snapshot)僅適用于查詢語句,對 DML(數據操縱語言,即增刪改操作)不適用。其他事務執行刪除或更新操作并提交,當前事務雖然 看不到 這些更改,但在執行自己執行更新或刪除操作后對其可見。雖然此注解足以解釋上述案例結論,但筆者實際上仍然存在前述表中的疑問。

最后需要指出的是,MVCC 機制是基于快照版本的并發控制,與之對應的是 LBCC,當采用 LBCC 讀取數據時,則總能讀到最新的數據。當然,這與 RR 隔離級別和 MVCC 機制并不矛盾。

加鎖讀總是讀取最新結果,但不影響快照版本

11. 快照版本

MVCC 是基于多版本的并發控制,查詢結果以快照版本為準。但不同隔離級別的快照版本采集原則不一致。在 RR 隔離級別中,通過 MVCC 機制實現了在同一事務中的可重復讀取問題,而且該快照是在首次查詢時采集的版本號信息,而與開啟事務時機無關。

RR 級別中首次查詢建立快照版本

而且,RR 級別中一旦建立了快照版本,則在該事務的后續查詢中均采用該快照版本作為結果(當然,通過前面的案例發現也有例外);與之對應的是,RC 級別中,每次查詢都采集最新的快照版本作為結果,所以自然也就存在不可重復讀的問題。

12. 加鎖類型

首先簡單介紹記錄鎖、間隙鎖和臨鍵鎖:

  記錄鎖

記錄鎖根據索引鎖定相應記錄,即使相應的表中不建立任何索引時。實際上所有 InnoDB 表都存在索引,當用戶建表時未顯式設置索引時,引擎會自動建立隱藏索引,這也是 InnoDB 底層基于聚簇索引存取整條記錄的特性使然。

記錄鎖僅對索引滿足查詢條件的記錄加鎖

  間隙鎖

如果說記錄鎖是對命中的記錄進行加鎖,那么間隙鎖是則是對查詢區間范圍內但是不存在的記錄進行預訂加鎖,例如下圖中假設表中不存在 id=2、3 的記錄,但因為滿足查詢范圍,所以會對其加間隙鎖。

MySQL 中的鎖怎么理解

間隙鎖對滿足查詢條件的記錄間隙加鎖

顯然,間隙鎖是以犧牲一定并發性能為代價換取高一致性。實際上,這也是所有鎖在做的一件事,即在一致性和并發能力之間獲得某種均衡。

需要指出的是:

鴻蒙官方戰略合作共建——HarmonyOS 技術社區

  間隙鎖僅在范圍查詢時存在,對于等值查詢則不適用,例如上例中查詢條件改為 where id=1 or id= 4 則不會對潛在的 id= 2 和 3 加間隙鎖

  當查詢條件是等值查詢,但查詢條件是聯合索引(在多列創建的索引)時,也會對滿足要求的潛在記錄加間隙鎖

  間隙鎖僅在特定隔離級別存在,RR 級別中默認有間隙鎖,而 RC 級別則不存在

  臨鍵鎖

在記錄鎖和間隙鎖的基礎上,臨鍵鎖 = 記錄鎖 + 間隙鎖。

MySQL 中的鎖怎么理解

臨鍵鎖 = 記錄鎖 + 間隙鎖

RC 隔離級別中只有記錄鎖,而沒有間隙鎖和臨鍵鎖;RR 級別中如果是等值查詢則是記錄鎖,范圍查詢則是臨鍵鎖(即記錄鎖 + 間隙鎖),在 5.6 以前版本中可以通過全局參數設置是否開啟,但在 8.0 版本已移除該變量。

 RC 隔離級別默認設置記錄鎖

MySQL 中的鎖怎么理解

 RR 隔離級別默認加臨鍵鎖

MySQL 中的鎖怎么理解

13. 索引類型對加鎖影響

在明確加鎖類型后,還需考慮不同索引對加鎖的影響。首先指出,在 InnoDB 引擎下即使創建表時不顯式指定索引,引擎也會自動生成隱藏索引用于聚簇存儲記錄數據。基于此,索引對加鎖的影響有如下幾種情況(引自官方文檔):

  一致讀(即快照讀,非加鎖讀,基于 MVCC),除 SE 隔離級別外,其他隔離級別均不加任何鎖

  當前讀(加鎖讀,for share 或 for update),對所有滿足條件的記錄加鎖,同時釋放不滿足條件的索。對于某些復雜語句,例如含有 Union 語句時,由于在匯總結果時涉及到臨時表,所以對于不滿足查詢條件的記錄不會立即釋放鎖。同時,加記錄鎖還是臨鍵鎖要取決于索引類型和查詢條件,只有當對應唯一索引下的等值查詢時,才只加記錄鎖,否則會升級為臨鍵鎖

 update 語句會對每條滿足記錄的語句加臨鍵鎖(X 鎖),但滿足唯一索引和等值查詢時,只加記錄鎖

 delete 語句加鎖原則與 update 語句一致

 insert 語句只對插入行加記錄鎖(X 鎖),而沒有任何間隙鎖。實際上,insert 語句是先加意向鎖,請求成功才去插入,否則也不會阻塞其他事務。特殊情況下,當多個事務同時 insert 相同索引記錄時,會發生索引重復沖突,進而可能造成死鎖。詳見下一節。

不同類型下的加鎖分析詳見文末參考資料 2 中文檔,講解充分,受到廣泛轉發引用,這里個人就不班門弄斧了。

14. 鎖競爭和死鎖

一般來說,鎖具有排他性。如果是共享鎖(S 鎖),可以和另一個共享鎖(S 鎖)同時擁有,但無法和一個排他鎖(X 鎖)同時擁有;而對于一個 X 鎖,則無法跟任何其他鎖并發。當多個事務企圖同時占用某一資源需要加鎖時,就有可能發生鎖競爭甚至死鎖。

  鎖競爭,當多個事務同時企圖占有同一資源、但只是時間上沖突而資源占用上并不沖突時,會發生鎖競爭:

MySQL 中的鎖怎么理解

多個事務競爭同一資源

在上述案例中,三個事務依次請求對數據表加 X 鎖,其中事務 A 成功請求,事務 B 和事務 C 會處于等待。當事務 A 提交事務后,雖然事務 B 和事務 C 處于同時競爭加鎖狀態,但由于 MySQL 對事務調度的 FIFO(First In First Out,先入先出)特性,二者不會發生死鎖,而是優先滿足事務 B 加鎖請求,待事務 B 提交事務后再滿足事務 C 的加鎖請求。

  死鎖,與鎖競爭相似而又不同的是,死鎖也是發生在多個事務同時競爭同一資源,但是這些資源不能簡單通過時間先后得以解決,而是存在邏輯上的沖突:

①,鎖競爭 + 索引重復沖突造成死鎖:

MySQL 中的鎖怎么理解

三個事務競爭資源存在索引重復

這個案例與鎖競爭中的例子類似但又不同:假設事務 A、事務 B 和事務 C 同時請求插入一條數據(插入語句都是加 X 鎖),此時不僅僅是因為加鎖沖突,還存在索引重復的問題,此時一旦事務 A 回滾釋放鎖后,事務 B 和事務 C 則會陷入死鎖。這是一種特殊的死鎖觸發原因。

②,競爭同一資源出現死循環:

MySQL 中的鎖怎么理解

兩個事務先競爭,后死鎖

在這個案例中,先是事務 A 和事務 B 分別對 id= 1 和 id= 2 的記錄加 X 鎖,然后事務 A 繼續對 id= 2 的記錄請求加鎖時,因為該記錄已被事務 B 占有,所以事務 A 只能等待;但此時事務 B 又企圖對事務 A 已經占有的 id= 1 記錄加 X 鎖,造成事務 A 和事務 B 在各自占有一定資源的基礎上分別企圖占用對方已加鎖的資源,邏輯上沖突,騎虎難下,引擎不可能通過時間調度得以解決,故而發生死鎖。

發生死鎖后,引擎會根據相關的事務間的重要程度(包括占用資源多少、時間先后等)來選擇一個進行回滾:例如上例中,事務 A 先于事務 B 請求加 X 鎖,可將事務 B 看成是直接造成死鎖的原因,所以選擇對 B 進行回滾,而允許 A 加鎖成功。

到此,相信大家對“MySQL 中的鎖怎么理解”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-17發表,共計8258字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 洛浦县| 盐池县| 鄄城县| 河北省| 桃源县| 彰化市| 岑巩县| 岑溪市| 大方县| 荔浦县| 长春市| 浮山县| 香河县| 华亭县| 泾阳县| 日土县| 平度市| 久治县| 遂宁市| 哈尔滨市| 禄劝| 保山市| 汕头市| 东乡| 新绛县| 伊川县| 平昌县| 玉屏| 新河县| 公主岭市| 武宁县| 剑阁县| 南雄市| 黑水县| 西安市| 陆丰市| 姚安县| 黄冈市| 三门县| 昆山市| 宝坻区|