中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL中的鎖怎么理解

發布時間:2021-12-03 17:19:12 來源:億速云 閱讀:157 作者:iii 欄目:數據庫

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

01. 怎么認識"鎖"

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

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

02. "鎖"的分類

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

  •  按照加鎖策略,可分為記錄鎖(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……)都加鎖,例如普通的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 ……;  2等價于  3START TRANSACTION;  4selece ……;  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隔離級別。

案例:

MySQL中的鎖怎么理解

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).

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

案例:

MySQL中的鎖怎么理解

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級別中的不可重復讀問題:

MySQL中的鎖怎么理解

RR不存在不可重復讀數據

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

MySQL中的鎖怎么理解

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

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

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

MySQL中的鎖怎么理解

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

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

MySQL中的鎖怎么理解

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

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

MySQL中的鎖怎么理解

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

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

MySQL中的鎖怎么理解

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

11. 快照版本

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

MySQL中的鎖怎么理解

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

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

12. 加鎖類型

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

  •  記錄鎖

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

MySQL中的鎖怎么理解

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

  •  間隙鎖

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

MySQL中的鎖怎么理解

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

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

需要指出的是:

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

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

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

  4.  間隙鎖僅在特定隔離級別存在,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中的鎖怎么理解”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

婺源县| 金堂县| 六枝特区| 水富县| 庄河市| 佳木斯市| 蒲江县| 南川市| 屏边| 崇礼县| 凤阳县| 普兰店市| 陵川县| 手机| 江油市| 乌拉特中旗| 涡阳县| 武山县| 黄梅县| 永昌县| 奉化市| 宽城| 桐柏县| 聂拉木县| 湖口县| 铜鼓县| 清新县| 绵阳市| 登封市| 云浮市| 昌邑市| 漯河市| 建昌县| 广宗县| 鄂温| 大渡口区| 吉安市| 敖汉旗| 拉孜县| 德格县| 常州市|