innodb實現了以下兩種型別的行鎖:
共享鎖(s):允許乙個事務去讀一行,阻止其他事務獲得相同的資料集的排他鎖。
排他鎖(x):允許獲得排他鎖的事務更新資料,阻止其他事務獲得相同資料集的共享鎖和排他鎖。
共享鎖就是我讀的時候,你可以讀,但是不能寫。排他鎖就是我寫的時候,你不能讀也不能寫。
另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,innodb還有兩種內部使用的意向鎖,這兩種意向鎖都是表鎖。
意向共享鎖(is):事務打算給資料行加行共享鎖,事物在給乙個資料行加共享鎖前必須先取得該錶的is鎖。
意向排他鎖(ix):事務打算給資料行加行排他鎖,事務在給乙個資料行加排他鎖前必須先取得該錶的ix鎖。
當乙個事務請求的鎖模式與當前的鎖相容,innodb就將請求的鎖授予該事務;反之如果請求不相容,則該事務就等待鎖釋放。
意向鎖是innodb自動加的,不需要使用者干預。
對於insert、update、delete,innodb會自動給涉及的資料加排他鎖(x);對於一般的select語句,innodb不會加任何鎖,事務可以通過以下語句給顯示加共享鎖或排他鎖。
共享鎖:select * from table_name where …..lock in share mode
排他鎖:select * from table_name where …..for update
加入共享鎖的:
加入排他鎖:
在mysql中,行級鎖並不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,mysql就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,mysql會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。
innodb行鎖是通過給索引項加鎖實現的,如果沒有索引,innodb會通過隱藏的聚簇索引來對記錄加鎖。也就是說,如果不通過索引條件檢索資料,那麼innodb將對錶中所有資料加鎖,實際效果跟表鎖一樣。
行鎖分為三種情形:
record lock:對索引項加鎖,即鎖定一條記錄。
gap lock:對索引項之間的『間隙』、對第一條記錄前的間隙或最後一條記錄後的間隙加鎖,即鎖定乙個範圍的記錄,不包含記錄本身
next-key lock:鎖定乙個範圍的記錄幷包含記錄本身(上面兩者的結合)。
注意:innodb預設級別是repeatable-read級別,所以下面說的都是在rr級別中的。
next-key lock是行鎖與間隙鎖的組合,這樣,當innodb掃瞄索引記錄的時候,會首先對選中的索引記錄加上行鎖(record lock),再對索引記錄兩邊的間隙加上間隙鎖(gap lock)。如果乙個間隙被事務t1加了鎖,其它事務是不能在這個間隙插入記錄的。
假設我們有一張表:
+----+------+
| id | age|
+----+------+
| 1 | 3 |
| 2 | 6 |
| 3 | 9 |
+----+------+
表結構如下:
create
table
`test` (
`id`
int(11) not
null auto_increment,
`age`
int(11) default
null,
primary
key (`id`),
key`keyname` (`age`)
) engine=innodb auto_increment=302
default charset=gbk ;
這樣我們age段的索引就分為
(negative infinity, 3],
(3,6],
(6,9],
(9,positive infinity);
我們來看一下幾種情況:
1、當事務a執行以下語句:
mysql> select * from fenye where age=6for update ;
不僅使用行鎖鎖住了相應的資料行,同時也在兩邊的區間,(5,6]和(6,9] 都加入了gap鎖。
這樣事務b就無法在這個兩個區間insert進新資料,但是事務b可以在兩個區間外的區間插入資料。
2、當事務a執行
select * from fenye where age=7 for update ;
那麼就會給(6,9]這個區間加鎖,別的事務無法在此區間插入或更新資料。
3、如果查詢的資料不再範圍內
比如事務a執行 select * from fenye where age=100 for update ;
那麼加鎖區間就是(9,positive infinity)。
行鎖防止別的事務修改或刪除,gap鎖防止別的事務新增,行鎖和gap鎖結合形成的的next-key鎖共同解決了rr級別在寫資料時的幻讀問題。
何時在innodb中使用表鎖?
innodb在絕大部分情況會使用行級鎖,因為事務和行鎖往往是我們選擇innodb的原因,但是有些情況我們也考慮使用表級鎖。
1、當事務需要更新大部分資料時,表又比較大,如果使用預設的行鎖,不僅效率低,而且還容易造成其他事務長時間等待和鎖衝突。
2、事務比較複雜,很可能引起死鎖導致回滾。
關於這部分內容,有一篇不錯的博文,貼上
mysql的innodb儲存引擎行鎖是加在索引上的,所以只當增刪改查操作是通過索引找到指定資料行的時候,才能對相應資料行的索引加鎖,否則只能對整個表加表鎖,表共享讀鎖或表獨佔寫鎖。
當乙個事務不經過索引查詢資料,即順序讀取(全表掃瞄)時,先獲取表的意向共享鎖,然後對錶新增共享讀鎖,阻止其他事務對錶的更新,新增和刪除操作,但不影響查詢操作,共享讀鎖之間是相容的。
當乙個事務不經過索引更新,刪除資料,即全表掃瞄符合條件的資料行時,先獲取表的意向獨佔鎖,然後對錶新增獨佔寫鎖,在執行更新,刪除時阻止其他事務對錶的讀及寫操作。
當乙個事務使用索引去查詢資料,即隨機讀取時,先獲得表的意向共享鎖,然後對符合條件的的索引區間加共享讀鎖(聚集索引肯定會加鎖,若用到了非聚集索引一樣加鎖),共享讀鎖之間是相容的,因此不影響其他事務對被鎖資料行的訪問。當其他事務想要修改,刪除加鎖的資料行時,若未使用索引則在獲取表獨佔寫鎖時會失敗。若使用索引檢索這些資料行,則可能會在非聚集索引處被阻塞(查詢事務和修改事務使用同乙個索引檢索資料行),也可能在聚集索引處被阻塞(通過非聚集索引查詢到聚集索引的鍵,通過此鍵查詢到相應的聚集索引,同時讀到資料航,這是讀和寫,寫和寫之間的事務序列化保證),只能等待查詢事務釋放索引區間的共享讀鎖,然後執行更新,刪除操作。當乙個事務想要將新的資料行插入到被加鎖的資料行中,也需要等待共享讀鎖的釋放。因為innodb實現了間隙鎖機制,即當乙個事務按乙個條件(id < 10,id列含有索引)加鎖資料行時,其他事務不能在鎖釋放前將符合此條件的資料行(id = 5)插入到表中,此機制一定程度防止了幻讀的出現。猜測實現機制:前乙個事務對資料行的索引加了鎖(聚集索引和非聚集索引),其他的事務插入資料時,需要新增聚集/非聚集索引,但是此時符合條件的聚集/非聚集索引區間已經被加鎖,不能實時插入索引,需要等到索引區間的鎖被釋放,這個可能是間隙鎖的實現原理。
當乙個事務通過索引去更新,刪除資料行時,先獲取表的意向排他鎖,然後對符合條件的資料行的索引加鎖(聚集/非聚集),阻止其他事務對被鎖資料行的讀,寫。同理實現間隙鎖。
MySQL儲存引擎 InnoDB
為什麼innodb不將總數存起來?innodb直接count 會遍歷全表 沒有where條件 雖然結果準確,但會導致效能問題。按照效率排序的話,count 字段 innodb一棵b 樹可以存放多少行資料?這個問題的簡單回答是 約2千萬。為什麼是這麼多呢?因為這是可以算出來的,要搞清楚這個問題,我們先...
MySQL的儲存引擎INNODB
1 一般情況下,mysql會預設提供多種儲存引擎,你可以通過下面的檢視 看你的mysql現在已提供什麼儲存引擎 mysql show engines 看你的mysql當前預設的儲存引擎 mysql show variables like storage engine 你要看某個錶用了什麼引擎 在顯示...
mysql的innodb儲存引擎
innodb是支援事務的儲存引擎,支援acid特性的acid 指資料庫事務正確執行的四個基本要素的縮寫 包含 原子性 atomicity 一致性 consistency 隔離性 isolation 永續性 durability 更適合處理大量的小事務,小事務正常都會被提交,很少會被回滾,在資料儲存的...