innoDB鎖小結

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加上索引,沒有走全表時,就沒有堵塞其它請求了。)各鎖之間的兼容狀況請看下圖:html

  image

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

如何查看鎖
一、經過show engine innodb status\G;
二、直接查詢試圖 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鎖劃分區間的時候,實際上是根據[輔助索引,彙集索引]組成的二維數組來劃分的。sql


阻塞:
給一個對象加鎖會阻塞其它對象對它的請求,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;這樣的檢索就會堵塞同時訪問該父表的其它事務的請求,從而影響性能。數據庫

 

轉自: 數組

https://www.cnblogs.com/janehoo/p/5603983.html安全

 

 

---tips

InnoDB的行鎖是經過給索引上的索引項加鎖來實現的。
只有經過索引條件進行數據檢索,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖(鎖住索引的全部記錄, S,X, IS,IX)併發

 

意向鎖的做用: mvc

當事務想去進行鎖表時,能夠先判斷意向鎖是否存在,存在時則可快速返回該表不能啓用表鎖.性能

舉個例子,若是表中記錄1億,事務A把其中有幾條記錄上了行鎖了,這時事務B須要給這個表加表級鎖,若是沒有意向鎖的話,那就要去表中查找這一億條記錄是否上鎖了。若是存在乎向鎖,那麼假如事務A在更新一條記錄以前,先加意向鎖,再加X鎖,事務B先檢查該表上是否存在乎向鎖,存在的意向鎖是否與本身準備加的鎖衝突,若是有衝突,則等待直到事務A釋放,而無須逐條記錄去檢測。事務B更新表時,其實無須知道到底哪一行被鎖了,它只要知道反正有一行被鎖了就好了。


說白了意向鎖的主要做用是處理行鎖和表鎖之間的矛盾,可以顯示「某個事務正在某一行上持有了鎖,或者準備去持有鎖」


 

記錄鎖,(Record), 間隙鎖(Gap) ,臨建鎖(Next-key)

 

Next-key locks:
鎖住記錄+區間(左開右閉)
當sql執行按照索引進行數據的檢索時,查詢條件爲範圍查找(between and、<、>等)並有數據命中則此時SQL語句加上的鎖爲Next-key locks,鎖住索引的記錄+區間(左開右閉)

Gap locks:
鎖住數據不存在的區間(左開右開)
當sql執行按照索引進行數據的檢索時,查詢條件的數據不存在,這時SQL語句加上的鎖即爲Gap locks,鎖住索引不存在的區間(左開右開)

Record locks:
鎖住具體的索引項當sql執行按照惟一性(Primary key、Unique key)索引進行數據的檢索時,查詢條件等值匹配且查詢的數據是存在,這時SQL語句加上的鎖即爲記錄鎖Record locks,鎖住具體的索引項

 

 

Gap鎖只能存在RR(Repeat Read隔離級別), 做用就是爲了防止幻讀.. 其中Gap鎖就是鎖住的兩條實際記錄之間的空隙, 而不是記錄自己.

 


 

 Undo log

快照讀:
SQL讀取的數據是快照版本,也就是歷史版本,普通的SELECT就是快照讀
innodb快照讀,數據的讀取將由 cache(本來數據) + undo(事務修改過的數據) 兩部分組成


當前讀:
SQL讀取的數據是最新版本。經過鎖機制來保證讀取的數據沒法經過其餘事務進行修改
UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE都是

 

 

Redo log


Redo log指事務中操做的任何數據,將最新的數據備份到一個地方 (Redo Log)

Redo Log實現事務持久性:
防止在發生故障的時間點,尚有髒頁未寫入磁盤,在重啓mysql服務的時候,根據redo log進行重作,從而達到事務的未入磁盤數據進行持久化這一特性。

 

一旦事務成功提交且數據持久化落盤以後,此時Redo log中的對應事務數據記錄就失去了意義,因此Redo log的寫入是日誌文件循環寫入的

指定Redo log日誌文件組中的數量 innodb_log_files_in_group 默認爲2
指定Redo log每個日誌文件最大存儲量innodb_log_file_size 默認48M
指定Redo log在cache/buffer中的buffer池大小innodb_log_buffer_size 默認16M

Redo buffer 持久化Redo log的策略,
Innodb_flush_log_at_trx_commit:
取值 0
每秒提交 Redo buffer --> Redo log OS cache -->flush cache to disk[可能丟失一秒內的事務數據]

取值 1 默認值,
每次事務提交執行Redo buffer --> Redo log OS cache -->flush cache to disk[最安全,性能最差的方式]

取值 2
每次事務提交執行Redo buffer --> Redo log OS cache 再每一秒執行 ->flush cache to disk操做


 

用戶插入,更新, 刪除操做, 在數據庫中的執行流程

 

從圖中,能夠看到,一個Update操做的具體流程。當Update SQL被髮給MySQL後,MySQL Server會根據where條件,讀取第一條知足條件的記錄,而後InnoDB引擎會將第一條記錄返回,並加鎖 (current read)。待MySQL Server收到這條加鎖的記錄以後,會再發起一個Update請求,更新這條記錄。一條記錄操做完成,再讀取下一條記錄,直至沒有知足條件的記錄爲止。所以,Update操做內部,就包含了一個當前讀。同理,Delete操做也同樣。Insert操做會稍微有些不一樣,簡單來講,就是Insert操做可能會觸發Unique Key的衝突檢查,也會進行一個當前讀。

引伸: 因此在RR加鎖策略時須要鎖定gap, 保證在同一個事務中讀取下一條記錄時, 不會有數據衝突.

相關文章
相關標籤/搜索