[小結]了解innodb鎖


原創文章,會不定時更新,轉發請標明出處:http://www.cnblogs.com/janehoo/p/5603983.html 

背景介紹:

innodb的鎖分兩類:lock和latch。
  其中latch主要是保證並發線程操作臨界資源的正確性,要求時間非常短,所以沒有死鎖檢測機制。latch包括mutex(互斥量)和rwlock(讀寫鎖)。
  而lock是面向事務,操作(表、頁、行)等對象,用來管理共享資源的並發訪問,是有死鎖檢測機制的。
現在我們要着重講的是innodb的lock鎖,下面我們先來介紹幾個概念。
  行鎖:innodb實現了多粒度鎖,作用對象為表則為表鎖,作用對象為行(Record)則為行鎖。其中行鎖包括共享行鎖和排他行鎖。
  共享行鎖(S):允許事務讀取一行數據。
  排他行鎖(X):允許事務刪除或更新一行數據。
  意向鎖:數據庫需要對細粒度的對象上鎖,需要首先給粗粒度的對象上鎖。在粗粒度對象上上的鎖成為意向鎖。innodb的意向鎖包括共享意向表鎖和排他意向表鎖。
  共享意向表鎖(IS):S鎖對應IS鎖
  排他意向表鎖(IX):X鎖對應IS鎖
  鎖兼容:兼容是指在同一對象上允許同種或不同種鎖同時存在。
lock鎖的分類
  innodb實現了行級鎖,包括行級共享鎖(S)及行級排它鎖(X),其中S和S是兼容的,其它都是不兼容的。所謂兼容是指對同一記錄(row)鎖的兼容性情況。innodb通過意向鎖實現多粒度鎖定。對innodb而言只有表意向共享鎖(IS)和表意向排他鎖(IX)。在給表加行鎖前,需要先對該表加意向鎖。因為意向鎖是表級別的,而innodb的其它鎖是行級別的,所以如果不是全表掃,意向鎖是不會對堵塞其它請求的。(小編這里特地做了一個實驗,發現select * from t1 where id=5 for update,此時id上面沒有索引,走的是全表掃描,確實堵塞了其它任何請求。但給id加上索引,沒有走全表時,就沒有堵塞其它請求了。)各鎖之間的兼容情況請看下圖:

  image

簡記:含I的鎖與含I的鎖是相互兼容的
  含I的S鎖與不含I的S鎖兼容
  不含I的鎖,含S的與含S的兼容與含X的不兼容
  不含I的鎖,含X的與任何鎖都不兼容

如何查看鎖
1、通過show engine innodb status\G;
2、直接查詢試圖 innodb_trx,innodb_locks,innodb_lock_waits;
關於讀請求的鎖
讀請求可以分為兩類:一致性鎖定讀和一致性非鎖定讀。
  一致性鎖定讀是指讀取數據的時候需要給表上加鎖,有兩種鎖定形式:
  select * from t1 for update;(加的是X鎖)
  select * from t1 in share mode;(加的是S鎖)
  而一致性非鎖定讀是指讀取數據的時候不給表加鎖,利用MVCC(multi version concurrency control)特性當讀取數據時如果碰到對象已經上了X鎖就直接讀取鏡像數據。又因為事務隔離級別的不同,在不同事務隔離級別下讀取的鏡像也會不同。
鎖的算法
鎖有三種算法:
  Record Lock:單個記錄上的鎖
  Gap Lock:間隙鎖(鎖定范圍但不包含記錄本身)
  Next-Key Lock:鎖定一個范圍並且包括記錄本身,Innodb對於行的查詢都采用這種算法(為了解決幻讀)。但查詢的索引含有唯一屬性(主鍵或唯一索引)時,innodb存儲引擎會對next-key lock進行優化,將其降級為Record Lock。即僅鎖住索引本身,而不是范圍。若是輔助索引則會分別對當前輔助索引及聚集索引加鎖定。對聚集索引采用Record Lock鎖定,而輔助索引則使用Next-Key Lock鎖定。需要注意的是如果是等值更新,innodb會對輔助索引值與前后值構成的范圍加上gap lock,而如果該輔助索引值不存在,則在該值所在區間上加上gap鎖。區間的划分和輔助索引包含的鍵值有關,如一個輔助索引包含了{1,3,5},則對應的區間有(-∞,1),(1,3),(3,5),(5,+∞)。例如更新值為2,則鎖定(1,3)這兩個區間,而如果更新值為3則鎖住(1,3),[3],(3,5)這個范圍。如果是范圍查詢的話,則鎖定的是該SQL涉及的范圍內的記錄和間隙。確切地說,其實輔助索引的葉子節點都包含了對應的聚集索引值,在使用gap鎖划分區間的時候,其實是根據[輔助索引,聚集索引]組成的二維數組來划分的。
阻塞:
給一個對象加鎖會阻塞其它對象對它的請求,innodb通過設置innodb_lock_wait_timeout來控制等待時間,並通過設置innodb_rollback_on_timeout來設置是否等待超時對事務進行回滾,默認不回滾,超過等待時間則拋出異常,由用戶判斷是該rollback還是commit。
死鎖:
死鎖是指兩個或兩個以上的事務在執行過程中,因爭奪鎖資源而造成的一種相互等待的現象。死鎖出現的概率是非常低的。innodb內置有死鎖檢查機制。當出現死鎖時會自動回滾占用undo資源少的事務。死鎖的檢測除了超時還有wait-for graph,如果圖中出現環形回路則表明存在死鎖。
鎖升級:
很多數據庫如:SQL server就有鎖升級的想象,但是innodb並沒有鎖升級。這是因為innodb根據事務訪問的每個頁對鎖進行管理,采用位圖方式,因此不管一個事務鎖住頁中的一行還是多個記錄,其開銷通常都是一樣的。
鎖涉及的三類問題:
臟讀:讀到未提交的數據(Read-ncommitted隔離級別);
不可重復讀:(Read-committed);
丟失更新:避免丟失更新的方式就是給select ... from ... 加上 for update;
鎖的常見的誤區
誤區一:select col1,col2 from table1 where col1='xxx' 或select count(*) from table1;會鎖表;
事實上這樣的select語句是不會對訪問的資源加鎖的。因為這樣的查詢會使用一致性非鎖定讀,它訪問的是資源的鏡像(此處用到的技術是mvcc即multi version concurrency control),所以不會堵塞其它事務也不會被其它事務堵塞(感興趣的同學可以通過實驗驗證,不清楚實驗方法的話可以私信我)。當然這只是一般的select語句。如果是如下這種格式的語句仍然會對訪問的資源加鎖:
select col1,col2 from table1 for update;(加X鎖)
select col1,col2 from table1 lock in share mode;(加S鎖)
關於什么是共享鎖(S)什么是互斥鎖(X),上面已經做了介紹。我們需要注意的是S鎖和S鎖是兼容的,S鎖和X鎖、X鎖和X鎖是不兼容的。這里說的兼容指的是不同事務對同一行(row)資源的訪問的兼容性。顯式加鎖的select請求,會堵塞其它資源對該表的意向鎖請求,從而堵塞其它請求。所以一般情況下,如無特殊需求,是不允許應用對select語句顯示加鎖的。
誤區二:update table1 set col1='xxx' where col2='xxx'不走索引對數據庫的性能沒有多大影響;
RR隔離級別下,當使用update語句時,首先該語句會對它所訪問的表加意向排它鎖(IX),如果update語句走了索引,那么它會使用行鎖(X) ,只鎖定訪問的記錄及間隙(想了解更多可以百度MySQL鎖的算法)。而如果它沒有走索引,就會進行全表掃,這時會給整個表上記錄加上排他鎖(X)。這句SQL就會堵塞所有其它會話對該表的加鎖請求,從而堵塞其它請求。但如果數據庫開啟了innodb_locks_unsafe_for_binlog,會觸發semi-consistent read對不滿足條件的記錄會釋放它上面的排它鎖,同時不加gap鎖。
RC隔離級別下,SQL會走聚集索引的全掃描過濾,由於過濾是在MySQL server層進行的。因為每條記錄無論是否滿足條件,都會被加上X鎖。但是出於效率考慮,mysql對於不滿足條件的記錄,會在判斷后放鎖,最終持有的,是滿足條件的記錄上的鎖,但是不滿足記錄上的加鎖/放鎖動作不會省略(優化違背了2PL約束)。綜上所述:使用當前讀的SQL都必須要走索引
誤區三:自增長鍵會在整個事務過程中,鎖住自增長值;
現在我們數據庫中的表的主鍵都是設為自增長的。很多同事認為,這樣會不會非常影響數據庫的插入效率。事實上使用自增長值確實會影響數據入庫的效率,當時mysql 5.1.22版本后,對數據庫的自增長設計做了很大的優化,性能已經得到了很大的提升。在5.1.22 之前的版本,使用的是auto_inc locking來生成自增長主鍵。它並不是在整個事務過程中鎖住自增長資源而是在要生產主鍵的SQL執行完后就釋放資源。這就是我們為什么會碰到,事務回滾后,自增長值確仍舊增大的原因。5.1.22及之后,使用了輕量級互斥量(mutex)來實現自增長,並通過inodb_atuoinc_lock_mode來控制自增長的模式。數據庫默認該參數值為1,一般情況下都是用mutex來控制自增長,只有當bulk inserts的時候才會使用auto_inc locking模式。
誤區四:使用外鍵加強約束,不會影響性能;
很多同事在設計表結構的時候喜歡使用外鍵,這里會給大家說明,為什么不建議大家使用外鍵。
如果使用外鍵,那么當子表需要更新或插入數據的時候會去檢索父表。問題就出現在,檢索父表的使用並不是使用的是一致性非鎖定讀,而是使用的一致性鎖定讀。
內部檢索會是下面的格式:select * from parent where ... lock in share mode;這樣的檢索就會堵塞同時訪問該父表的其它事務的請求,從而影響性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM