mysql三種鎖

不使用索引

CREATE TABLE `test1` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

BEGIN;
-- 手動開啓一個事務,並在id = 1這條數據上加上排它鎖
SELECT * from test1 WHERE id = 1 for UPDATE;

BEGIN;
-- 手動開啓另外一個事務,此時給id=2的這條數據進行加排它鎖,結果會如何?
SELECT * from test1 WHERE id = 2 for UPDATE

發現此時居然查詢id=2的數據事務被卡住了。這是爲什麼呢?當表沒有創建索引時或者查詢語句沒有命中索引時,鎖住的是整個表的數據,因爲沒有命中索引故其會去掃描全表數據。 當一張表沒有索引時,innoDB會創建一個隱藏主鍵索引,當通過隱藏的主鍵索引去檢索時,將該表中所有的隱藏索引檢索一遍 例子:如果手動開始事務,並在id=1的數據上手動加上排它鎖.如果此時再去查詢id=2的數據時,發現此語句卡住了。 故得出沒有建立索引的表,一旦鎖住數據及爲鎖住整張表。

主鍵索引

CREATE TABLE `test2` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

BEGIN;
-- 主鍵索引
SELECT * from test2 WHERE id = 1 FOR UPDATE;

BEGIN;
-- 手動開啓其他事務
SELECT * from test2 WHERE id = 5 for update;

 此時說明,主鍵索引時只會鎖住匹配到的索引項,而不會影響其他事務操作其他索引

唯一索引

CREATE TABLE `test3` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

BEGIN;
-- 唯一索引
SELECT * from test3 where name = '李四' FOR update;

BEGIN;
-- 唯一索引
SELECT * from test3 where id= 5 FOR update;

 注意:此時SELECT * from test3 where id= 5 FOR update爲什麼執行卡住了?唯一索引鎖定時,先通過唯一索引然後找到對應主鍵索引,也就是輔助索引--->主鍵索引的一個過程,所以查詢id = 5的數據時也被鎖住了。 通過上面幾個例子發現mysql innodb是通過鎖住索引來實現行鎖的

mysql innodb 爲什麼不會出現幻讀?

詳見下面InnoDB的行鎖,如下圖 臨界鎖的操作 

臨界鎖(Next-key Lock)鎖定範圍加記錄

BEGIN;
-- 臨界鎖,鎖住對應的範圍,防止幻讀。
-- 按道理此時應該鎖住,6,7,8(已存在),9,10
SELECT * from test2 WHERE id > 5 and id <11 FOR UPDATE;

BEGIN;
-- 此時測試插入id=7 的值,按道理應該插入不進去,因爲鎖住的範圍是(5,8]和(8,12]
insert into test2 VALUES(7,'試試');

BEGIN;
SELECT * from test2 WHERE id = 12 FOR UPDATE;

剛纔不是id>5 and id <11的麼?此時爲什麼id =12也被鎖了呢?因爲此時鎖住的範圍是(5,8]和(8,12]

Gap Lock(間隙鎖)

BEGIN;
-- 間隙鎖,因爲id =7 這條數據不存在,故鎖退化成了間隙鎖,那麼此時id=7 落在了(5,8)這個區間
SELECT * from test2 where id = 7 for UPDATE;

BEGIN;
-- 因爲鎖退化成了間隙鎖,那麼此時id=7 落在了(5,8)這個區間,故id =6 也一起被鎖住了
INSERT into test2 VALUES(6,'卡卡');

Record Lock(記錄鎖)

注意: 測試在test2表中,也就是主鍵索引。

BEGIN;
-- 在事務1中在id =5 的主鍵項鎖定
SELECT * from test2 where id = 5 for update;

 

本文是摘抄的,原文路徑: https://my.oschina.net/u/3370769/blog/3000934

 

自己補充點東西把,自己不理解的地方也做下筆記。

間隙鎖(gap lock),鎖住的是一個區間範圍,比如鎖住區間(1,3),如果插入2的數據是插入不進去的,1和3是開區間,因此,1和3不受影響。

臨界鎖(next key lock),鎖住索引上的記錄+前面的間隙鎖,是一個左開右閉區間,比如(1,3]。一個next-key鎖=索引上的記錄鎖+鎖住前面間隙的gap lock。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章