• InnoDB存儲引擎支持行級鎖,其大類能夠細分爲共享鎖和排它鎖兩類
• 共享鎖(S):容許擁有共享鎖的事務讀取該行數據。當一個事務擁有一行的共享鎖時,另外的事務能夠在同一行數據也得到共享鎖,但另外的事務沒法得到同一行數據上的排他鎖
• 排它鎖(X):容許擁有排它鎖的事務修改或刪除該行數據。當一個事務擁有一行的排他鎖時,另外的事務在此行數據上沒法得到共享鎖和排它鎖,只能等待第一個事務的鎖釋放
• 除了共享鎖和排他鎖以外,InnoDB也支持意圖鎖。該鎖類型是屬於表級鎖,代表事務在後期會對該表的行施加共享鎖或者排它鎖。mysql
意圖鎖也有兩種類型:
• 共享意圖鎖(IS):事務將會對錶的行施加共享鎖
• 排他意圖鎖(IX):事務將會對錶的行施加排它鎖sql
例:數據庫
select … for share mode語句就是施加了共享意圖鎖,而select … for update語句就是施加了排他意圖鎖
• 這四種鎖之間的相互共存和排斥關係以下性能
InnoDB鎖相關係統表spa
Information_schema.innodb_trx記錄了InnoDB中每個正在執行的事務,包括該事務得到的鎖信息,事務開始時間,事務是否在等待鎖等信息設計
Information_schema.innodb_locks記錄了InnoDB中事務在申請但目前尚未獲取到的每一個鎖信息,以及當前事務的鎖正在阻止其餘事務得到鎖3d
Information_schema.innodb_lock_waits記錄了InnoDB中事務之間相互等待鎖的版本控制
• 行級鎖
• 行級鎖是施加在索引行數據上的鎖日誌
當一個InnoDB表沒有任何索引時,則行級鎖會施加在隱含建立的聚簇索引上,因此說當一條sql沒有走任何索引時,那麼將會在每一條彙集索引後面加X鎖orm
• 間隔鎖
• 當 咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件 的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個「間隙」加鎖
• 間隔鎖是施加在索引記錄之間的間隔上的鎖,鎖定一個範圍的記錄、但不包括記錄自己,好比SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE語句,儘管有可能對c1字段來講當前表裏沒有=15的值,但仍是會阻止=15的數據的插入操做,是由於間隔鎖已經把索引查詢範圍內的間隔數據也都鎖住了
• 間隔鎖的使用只在部分事務隔離級別纔是生效的
• 間隔鎖只會阻止其餘事務的插入操做
gap lock的前置條件:
• 1 事務隔離級別爲REPEATABLE-READ,innodb_locks_unsafe_for_binlog參數爲0,且sql走的索引爲非惟一索引(不管是等值檢索仍是範圍檢索)
2 事務隔離級別爲REPEATABLE-READ,innodb_locks_unsafe_for_binlog參數爲0,且sql是一個範圍的當前讀操做,這時即便不是非惟一索引也會加gap lock
例:
連接1:
mysql> update temp set name='abc' where id>4;
• 連接2:
• mysql> insert into temp values(4,‘abc’); ##等待
• Next-key鎖
• 在默認狀況下,mysql的事務隔離級別是可重複讀,而且innodb_locks_unsafe_for_binlog參數爲0,這時默認採用next-key locks。所謂Next-Key Locks,就是記錄鎖和間隔鎖的結合,即除了鎖住記錄自己,還要再鎖住索引之間的間隙。
• 插入意圖鎖
• 插入意圖鎖是在插入數據時首先得到的一種間隔鎖,對這種間隔鎖只要不一樣的事務插入的數據位置是不同的,雖然都是同一個間隔,也不會產生互斥關係
例:
• 好比有一個索引有4和7兩個值,若是兩個事務分別插入5和6兩個值時,雖然兩個事務都會在索引4和7之間施加間隔鎖,但因爲後續插入的數值不同,因此二者不會互斥
• 好比下例中事務A對索引>100的值施加了排他間隔鎖,而事務B在插入數據以前就試圖先施加插入意圖鎖而必須等待
• 自增鎖
• 自增鎖是針對事務插入表中自增列時施加的一種特殊的表級鎖,即當一個事務在插入自增數據時,另外一個事務必須等待前一個事務完成插入,以便得到順序的自增值
• 參數innodb_autoinc_lock_mode能夠控制自增鎖的使用方法
•innodb死鎖
• 死鎖的狀況發生在不一樣的的事務相互之間擁有對方須要的鎖,而致使相互一直無限等待
• 死鎖可能發生在不一樣的事務都會對多個相同的表和相同的行上施加鎖,但事務對錶的操做順序不相同
• 爲了減小死鎖的發生,要避免使用lock table語句,要儘可能讓修改數據的範圍儘量的小和快速;當不一樣的事務要修改多個表或者大量數據時,儘量的保證修改的順序在事務之間要一致
• 默認狀況下InnoDB下的死鎖自動偵測功能是開啓的,當InnoDB發現死鎖時,會將其中的一個事務做爲犧牲品回滾。(InnoDB選擇犧牲的事務每每是代價比較小的事務,其代價計算是根據事務insert,update, delete的數據行規模決定)
• 能夠經過show engine innodb status語句查看最後一次發生死鎖的狀況(能夠經過innodb_print_all_deadlocks參數將死鎖信息打印到錯誤日誌中)
減小死鎖的發生
• 儘量的保持事務小型化,減小事務執行的時間能夠減小發生影響的機率
• 及時執行commit或者rollback,來儘快的釋放鎖
• 能夠選用較低的隔離級別,好比若是要使用select... for update和select...lock in share mode語句時可使用讀取提交數據隔離級別
• 當要訪問多個表數據或者要訪問相同表的不一樣行集合時,儘量的保證每次訪問的順序是相同的。好比能夠將多個語句封裝在存儲過程當中,經過調用同一個存儲過程的方法能夠減小死鎖的發生
• 增長合適的索引以便語句執行所掃描的數據範圍足夠小
• 儘量的少使用鎖,好比若是能夠承擔幻讀的狀況,則直接使用select語句,而不要使用select...for update語句
• 若是沒有其餘更好的選擇,則能夠經過施加表級鎖將事務執行串行化,最大限度的限制死鎖發生
InnoDB事務隔離級別
• InnoDB存儲引擎提供了四種事務隔離級別,分別是:
• READ UNCOMMITTED:讀取未提交內容
• READ COMMITTED:讀取提交內容
• REPEATABLE READ:可重複讀,默認值。
• SERIALIZABLE:串行化
更改事物隔離級別語法格式爲:
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{
READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SERIALIZABLE
}
• REPEATABLE READ:可重複讀,默認值。代表對同一個事務來講第一次讀數據時會建立快照,在事務結束前的其餘讀操做(不加鎖)會得到和第一次讀相同的結果。當讀操做是加鎖的讀語句(select … for update或者lock in share mode),或者update和delete語句時,加鎖的方式依賴於語句是否使用惟一索引訪問惟一值或者範圍值
• 當訪問的是惟一索引的惟一值時,則InnoDB會在索引行施加行鎖
• 當訪問惟一索引的範圍值時,則會在掃描的索引行上增長間隔鎖或者next-key鎖以防止其餘連接對此範圍的插入
注:
對可重複讀隔離級別來講,第一個事務的修改會在每行記錄上都增長排他鎖,而且直到事務結束後鎖纔會釋放
第二個事務會一直等待前面事務的鎖被釋放後才能執行
• READ COMMITTED:讀取提交內容。意味着每次讀都會有本身最新的快照。對於加鎖讀語句(select … for update和lock in share mode),或者update,delete語句會在對應的行索引上增長鎖,但不像可重複讀同樣會增長間隔鎖,所以其餘的事務執行插入操做時若是是插入非索引行上的數值,則不影響插入。
• 因爲該隔離級別是禁用間隔鎖的,因此會致使幻讀的狀況
• 若是是使用此隔離級別,就必須使用行級別的二進制日誌
此隔離級別還有另外的特色:
• 對於update和delete語句只會在約束條件對應的行上增長鎖
• 對update語句來講,若是對應的行上已經有鎖,則InnoDB會執行半一致讀的操做,來肯定update語句對應的行在上次commit以後的數據是否在鎖的範圍,若是不是,則不影響update操做,若是是,則須要等待對應的鎖解開
注:
對讀取提交內容事務隔離級別來講,第一個修改操做會在全部行上都加排他鎖,但會在肯定不修改上的行上釋放對應的鎖
第二個事務經過半一致讀的方式判斷每行的最後commit的數據是否在修改的範圍裏,會在未加鎖的行上加上排他鎖
• READ UNCOMMITTED:讀取未提交內容,所讀到的數據多是髒數據
• SERIALIZABLE:串行化,此隔離級別更接近於可重複讀這個級別,只是當autocommit功能被禁用後,InnoDB引擎會將每一個select語句隱含的轉化爲select … lock in share mode
一致讀
在默認的隔離級別下一致讀是指InnoDB在多版本控制中在事務的首次讀時產生一個鏡像,在首次讀時間點以前其餘事務提交的修改能夠讀取到,而首次讀時間點以後其餘事務提交的修改或者是未提交的修改都讀取不到
• 惟一例外的狀況是在首次讀時間點以前的本事務未提交的修改數據能夠讀取到
• 在讀取提交數據隔離級別下,一致讀的每一個讀取操做都會有本身的鏡像
• 一致讀操做不會施加任何的鎖,因此就不會阻止其餘事務的修改動做
• 一致讀在某些DDL語句下不生效:
• 碰到drop table語句時,因爲InnoDB不能使用被drop的表,因此沒法實現一致讀
• 碰到alter table語句時,也沒法實現一致讀
• 當碰到insert into… select, update … select和create table … select語句時,在默認的事務隔離級別下,語句的執行更相似於在讀取提交數據的隔離級別下
InnoDB 的 MVCC ,是經過在每行記錄後面保存兩個隱藏的列來實現的。這兩個列一把保存了行的建立時間,一個保存行的過時時間(或刪除時間),固然存儲的並非真正的時間,而是系統版本號。每開始一個事務,系統版本號就會自動遞增,事務開始時刻的版本號做爲當前事務的版本號,用來和查詢到的每行記錄的版本號就行比較。
如下是 REPEATABLE READ 的隔離級別下具體操做:
保存着兩個額外的系統版本號,使大多數讀操做均可以不用加鎖。這樣設計使得讀數據操做很簡單,性能很好,而且也能保證只會讀取到符合標準的行
Autocommit/commit/rollback
• 當設置autocommit屬性開啓時,每一個SQL語句都會隱含成爲獨立的事務。
• 默認狀況下autocommit屬性是開啓的,也就意味着當每一個SQL語句最後執行結果不返回錯誤時都會執行commit語句,當返回失敗時會執行rollback語句
• 而當autocommit屬性開啓時,能夠經過執行start transaction或者begin語句來顯示的開啓一個事務,而事務裏能夠包含多個SQL語句,最終事務的結束是由commit或者rollback終結
• 而當在數據庫連接裏執行set autocommit=0表明當前數據庫連接禁止自動提交,事務的終結由commit或者rollback決定,同時也意味着下一個事務的開始
• 若是一個事務在autocommit=0的狀況下數據庫連接退出而沒有執行commit語句,則這個事務會回滾
• 一些特定的語句會隱含的終結事務,就比如是執行了commit語句
• commit語句表明將此事務的數據修改永久化,並對其餘事務可見,而rollback則表明將此事務的數據修改回滾。
• commit和rollback都會把當前事務執行所施加的鎖釋放