MySQL 鎖與隔離級別

2021-10-02 21:19:35 字數 4493 閱讀 3023

五、gap鎖^[1]^

六、next-key lock

七、如何選擇隔離級別

ref快照讀的幻讀通過 mvcc 解決

當前讀的幻讀通過 next-key鎖 解決

讀提交隔離級別一般沒有 gap lock

可重複讀隔離級別下,如果觸發了當前讀,那也是要保證事務存續期間的資料一致性的,具體怎麼保證呢?答案是加鎖,阻塞破壞本事務資料一致性的其他事務。

行鎖為innodb引擎獨有的,即行鎖是引擎層實現的

(一) 索引與鎖

可重複讀時:

innodb只有在訪問行的時候才會對其加鎖,而索引能夠減少innodb訪問的行數,從而減少鎖的數量。但這只有當innodb在儲存引擎層能夠過濾掉所有不需要的行時才有效。如果索引無法過濾掉無效的行,那麼在innodb檢索到資料並返回給伺服器層以後, mysql 伺服器才能應用where子句。 這時已經無法避免鎖定行了: innodb 已經鎖住了這些行,事務提交時才會釋放。即在沒有使用索引的情況下,innodb會鎖住整張表

example:

name欄位無索引:

+----+---------------------+------+

| id | dat | name |

+----+---------------------+------+

| 1 | 2020-01-24 12:00:55 | *** |

| 2 | 2020-01-24 12:01:34 | yyy |

| 3 | 2020-02-23 12:25:24 | zzz |

+----+---------------------+------+

session a:

start transaction with consistent snapshot;

update tb_date set name='zzz' where id=3;

session b:

update tb_date set name='yyy' where name='yyy';

其中session b會被阻塞住,原因是session a鎖住了 id=3 這一行,而session b試圖對所有行進行加鎖

讀提交時:

在mysql 5.1和更新的版本中,innodb可以在伺服器端過濾掉行後就釋放鎖,該特性存在於讀提交隔離級別下,可重複讀無該特性

gap鎖之間不互斥,與其他鎖之間也不互斥,只與 insert 操作互斥,gap鎖用於鎖間隙。

讀提交隔離級別一般沒有gap lock( 不過也有例外情況, 比如insert 出現主鍵衝突的時候,也可能加間隙鎖?在外鍵場景下還是有間隙鎖?)

example:

有**

create table `t` (

`id` int(11) not null,

`c` int(11) default null,

`d` int(11) default null,

primary key (`id`),

key `c` (`c`)

) engine=innodb;

insert into t values(0,0,0),(5,5,5),

(10,10,10),(15,15,15),(20,20,20),(25,25,25);

執行語句:

session a:

mysql> begin;

mysql> select d from t where c=5 for update;

或mysql> select id from t where c=5 for update;

session b:

mysql> insert into t values (8, 8, 8);

上述的 session b 會被 session a 阻塞,其原因在於,為保持session a 事務存續期間的資料一致性,innodb給範圍 c(0, 10) 之間加了間隙鎖(因為這個範圍內是有可能被其他事務插入c=5的行的)。

總結:session a 給id=5這一行加了行鎖,同時在索引頁c上加了範圍為(0, 10)的 gap 鎖 (注:該語句不會在聚簇索引頁上加gap鎖)

覆蓋索引的優化:

執行語句:

session a:

mysql> begin;

mysql> select id from t where c=5 lock in share mode;

session b:

mysql> update t set d=d+1 where c=5;

session b 不會被阻塞,原因是只有訪問到的物件才會加鎖,session a查詢的是id,可以直接使用c索引頁上的覆蓋索引獲取結果,因此沒有在聚簇索引頁上對 id=5 這一行加鎖,但如果 session a 執行的是 for update 就不一樣了,因為系統會認為你接下來要更新資料,因此會順便給主鍵索引上滿足條件的行加上行鎖。

note:

在使用索引查詢的情況下,若所查詢的行不在,則不會加行鎖,但還是會在合適的範圍內加上gap鎖,這依然是為了保證事務存續期間的資料一致性

若不使用索引查詢,則會走全表查詢,innodb會將所有行加上行鎖,同時會對所有間隙加上gap鎖,事實上,innodb不會去隱式的呼叫表級鎖,鎖整張表就是通過這種方式實現的

間隙鎖和行鎖合稱 next-key lock,每個 next-key lock 是前開後閉區間,譬如語句

mysql> select d from t where c=5 for update;
加了 next-key鎖(0, 5] 和 gap鎖(5, 10)

加鎖及優化規則[2]:

規則1:innodb加鎖的基本單位是 next-key lock。next-key lock 是前開後閉區間

規則2:查詢過程中訪問到的物件才會加鎖

優化1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock 退化為行鎖

優化2:索引上的等值查詢,向右遍歷時且最後乙個值不滿足等值條件的時候,next-key lock 退化為間隙鎖

規則3:範圍查詢,向右遍歷查詢時,先確定左邊界的 next-key 鎖,對於唯一性索引,向左搜尋到第乙個滿足條件的值為止,對於非唯一索引,向左搜尋到第乙個不滿足條件的值為止;再確定右邊界的next-key鎖,不管索引是否唯一,都是向右搜尋到第乙個不滿足條件的值為止。向左遍歷查詢時則反之。

例如:

mysql> select * from t where id>=10 and id<=20 for update;
先確定左邊界,id為唯一性索引,向左搜尋到第乙個滿足條件的值10,因此加上next-key lock (10, 15],因為id>=10隱含著等值查詢id=10,根據優化1,需在加上行鎖10,再確定右邊界,向右搜尋到第乙個不滿足條件的值25,加next-key鎖 (20, 25],得出該語句的加鎖範圍為 10, (10, 15], (15, 20], (20, 25]

再例如:

mysql> select * from t where c>=10 and c<=20 for update;
先確定左邊界,c為非唯一性索引,向左搜尋到第乙個不滿足條件的值5,因此加上next-key lock (5, 10],再確定右邊界,向右搜尋到第乙個不滿足條件的值25,加next-key鎖 (20, 25],得出該語句的加鎖範圍為 (5, 10], (10, 15], (15, 20], (20, 25]

規則4:limit 可減小加鎖範圍[2][3]

規則5:order by desc 是會改變遍歷方向的哦[2]!

note:我們在分析加鎖規則的時候可以用 next-key lock 來分析。但是要知道,具體執行的時候,是要分成間隙鎖和行鎖兩段來執行的,先加gap鎖,再加行鎖[2]。

網際網路專案中一般把隔離級別設為讀提交,然後把binlog的格式設為row[4] 。

[1] mysql中的gap鎖(間隙鎖)

[2] (極客時間)為什麼我只改一行的語句,鎖這麼多?

[3] mysql 中關於gap lock / next-key lock 的乙個問題

[4] 網際網路專案中mysql應該選什麼事務隔離級別

Mysql 鎖 隔離級別

1.共享鎖 只要有乙個人 事務或執行緒 在查詢,其他人就都不能改,要照顧少數 下面是將所有性別是男的資料都加上共享鎖 select from table where 男 lock in share mode2.排他鎖 只要有乙個人 事務或執行緒 在用 增刪改差 其他人就不能用 下面是將所有性別是男的...

mysql 鎖,隔離級別

1.鎖是對多執行緒,多程序訪問同一資源進行協調的機制 2.分為行鎖,表鎖,頁鎖。myisam和memory支援表鎖,bdb引擎支援表鎖,頁鎖。innodb支援表鎖,行鎖 預設 3.myisam表鎖 4.myisam併發插入 5.讀寫操作同時爭搶鎖,寫鎖優先,可以設定寫鎖優先順序降低,low prio...

MySQL隔離級別,鎖與MVCC

篇幅有限,相關概念可先閱讀 本文意在弄清楚這些概念間的關係及其作用。弄清mysql在開啟事務的情況下,每條sql執行時的加鎖操作和mvcc版本控制。為使討論簡單,本文忽略了gap鎖 間隙鎖 範圍鎖 我們經常所高併發,高可用。就是從質和量來評估,任何事物都可以從這兩個角度來分析。在mysql資料庫中,...