mysql锁的一些理解简书_MySQL锁系列之锁的种类和概念
在mysql當中,關于innodb的鎖類型總共可以分為四種,包含了行鎖和表鎖,分別是
基本鎖 - [ 共享鎖(Shared Locks:S鎖)和排它鎖(Exclusive Locks:X鎖)]
意向鎖 - [ intention lock,分為意向共享鎖(IS鎖)和意向排他鎖(IX鎖)]
行鎖 - [ record Locks、gap locks、next-key locks、Insert Intention Locks ]
自增鎖 - [ auto-inc locks ]
下面是各種鎖之間的對應兼容情況(ps:在某一篇博客上看到,忘了是哪一篇,覺得好就截下來了嘻嘻):
InnoDB三種行鎖的算法:
Record Lock:單個行記錄上的鎖,只鎖定記錄本身
Gap Lock:間隙鎖,鎖定一個范圍,但不包括記錄本身。 目的是為了防止同一個事物的兩次當前讀,出現幻讀的情況
Next-Key Lock:1+2,鎖定一個范圍,并鎖定記錄本身。目的:解決幻讀
共享鎖
共享鎖shared locks(S鎖)也稱讀鎖,允許其他事物再加S鎖,不允許其他事物再加X鎖
加鎖方式:
select...lock in share mode
注意:
對于使用共享鎖的事務,其他事務只能讀,不可寫
如果執行了更新操作則會一直等待,直到當前事務commit或者rollback
如果當前事務也執行了其他事務處于等待的那條sql語句,當前事務將會執行成功,而其他事務會報死鎖
并且允許其他鎖共存
排它鎖
排它鎖Exclusive Locks(X鎖)也稱寫鎖,不允許其他事務再加S鎖或者X鎖
加鎖方式:
select ... for update
→ for update:InnoDB默認是行級別的鎖,當有明確指定的主鍵時,使用的是行鎖;否則使用的是表鎖。使用情況詳細如下:
明確指定主鍵,并且由此記錄,行級鎖。例:
select name,age from tb_user where id = '1' for update(id是主鍵)
明確指定主鍵/索引,若查無記錄,無鎖。例:
select name,age from tb_user where id = '1' for update(id是主鍵,但不存在id = 1的數據)
無主鍵/索引,表級鎖。例:
select name,age from tb_user where age = 12 for update(age是普通字段)
主鍵/索引不明確,表級鎖。例:
select name,age from tb_user where age = 12,id = '1' for update(id是主鍵,age不是,但數據庫有此數據)
注意:
對于排它鎖的事務,其他事物可讀,但不可進行更新操作
for update僅使用與InnoDB,并且必須開啟事務,在begin和commit之間才生效
當一個事務進行for update的時候,另一個事務也有for update時會一直等待,直到之前的事務commit或rollback或斷開連接釋放鎖才拿到鎖進行后面的操作(排它鎖不能共存)
innoDB引擎.默認對update,delete,insert加排他鎖,select語句默認不加鎖
樂觀鎖
讀取出記錄,并將此版本一同讀出,執行更新操作并對記錄的版本號+1。此時,將待提交記錄的版本號與數據庫對應表的記錄版本好進行對比,如果大于數據庫原有版本號的話,予以更新;否則認為是過期數據,更新失敗。目的是為了用于解決并發問題。
例:A、B兩個人同時修改同一條記錄,設數據庫原有金額是100元,A對金額+100,B往數據庫-50,正常結果是150,但由于并發,結果有可能是200,或者50
解決:A B同時讀取出數據版本為1,A對金額+100,并修改數據版本為2,提交數據,此時數據版本為1,更新成功。B讀取數據版本1,對金額-50,此時結果為50,并修改數據版本為2,提交數據,對比數據庫原版本2,沒有比原版本高,更新失敗
間隙鎖
間隙鎖是在索引記錄之間的間隙的鎖定,或在最后一個索引記錄之前或之后的間隙上的鎖定
使用唯一索引搜索唯一一行的一句不需要間隙鎖鎖定(不包括搜索條件包含多列唯一索引的某些列的情況,查詢出的多條記錄,會發生間隙索引),詳細例子如下:
前提:(id是主鍵索引)由于搜索結果是唯一的一條記錄,所以不會使用間隙鎖
select id,name,age from tb_user where id = '1'
前提:(id是主鍵索引、age是非索引字段)由于搜索結果可能不止一條記錄,所以會使用間隙鎖select id,name,age from tb_user where id = '1' and age = 13
演示:
→ 數據結構:
CREATE TABLE `tb_user` (
`id` int(10) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
KEY `index_id` (`id`),
KEY `index_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
→ 初始化數據:
ps:我現在使用的是數據庫默認的隔離級別:repeatable read,并在本地開啟兩個客戶端進行測試。
→ 客戶端1:開啟事務,在客戶端中修改id為1-6之間的數據,此時id=2這行記錄是不存在的
→ 客戶端2:開啟事務,往數據庫中添加id為2的記錄時會發現該操作會被阻塞!
-->上述情況就說明了有間隙鎖的存在
--> 接下來我修改了隔離級別為read commited,可以發現上述添加操作,即id =2 的記錄會添加成功,說明read commited的隔離級別不會使用間隙鎖。
注意:
間隙鎖在InnoDB的唯一作用就是防止其它事務的插入操作,以此來達到防止幻讀的發生,所以間隙鎖不分什么共享鎖與排它鎖。
如果InnoDB掃描的是一個主鍵/唯一索引,那么InnoDB只會采用行鎖(Record Lock)方式來加鎖,而不會使用間隙鎖(Next-Key Lock)的方式。
間隙鎖只是阻止其他事物插入到間隙當中,并不阻止不同的事物在同一間隙上獲得間隙鎖。
將隔離級別設置為read_commited或啟用innodb_locks_unsafe_for_binlog系統變量(現已被棄用)可明確禁止使用間隙鎖
MVCC(Snapshot read vs current read)
MVCC,基于多版本的并發控制協議,最典型的是讀不加鎖,讀寫不沖突,其包含兩種讀操作,即快照讀(snapshot read)與當前讀(current read)。
快照讀:讀取記錄的可見版本,不加鎖。
當前讀:讀取記錄的最新版本,當前讀返回的記錄,都會加鎖,保證其他事物不會再修改這條記錄
那具體哪些操作為當前讀,哪些操作又是快照讀呢,讓我們來看一下:
→ 快照讀:簡單的讀操作,屬于快照讀,不加鎖。(不過有些會有點小例外)
例:select * from tb_user where ?
→ 當前讀:特殊的讀操作,屬于當前讀,需要加鎖。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
所有以上的語句,都屬于當前讀,讀取記錄的最新版本。并且,讀取之后,還需要保證其他并發事務不能修改當前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加S鎖 (共享鎖)外,其他的操作,都加的是X鎖 (排它鎖)。
總結
以上是生活随笔為你收集整理的mysql锁的一些理解简书_MySQL锁系列之锁的种类和概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: down 网卡端口周期性的up_down
- 下一篇: linux无法访问mysql_Linux