持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
1.1、概述
读锁/共享锁(Share lock,即S锁)
共享锁(S):又称写锁,允许获取排它锁的事物更新数据,阻止其他事务取得相同的数据集共享读锁和排它写锁,若事务T对数据对象A加上X锁,事物T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T 释放A上的锁。
写锁/排他锁(Exclusive lock,即X锁)
排它锁(X):又称写锁,允许获取排它锁的事物更新数据,阻止其他事务取得相同的数据集共享读锁和排它写锁,若事务T对数据对象A加上X锁,事物T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T 释放A上的锁。
1.2、锁定读操作
MySQL中 SELECT语句读取记录的加锁格式
- SELECT语句读取记录加S锁
select ... lock in share mode;
-- 或
select ... for share;(8.0新增)
在普通的SELECT语句后边加LOCK IN SHARE MODE,如果当前事务执行了该语句,那么它会为读取到的记录加s锁,这样允许别的事务继续获取这些记录的s锁(比方说别的事务也使用SELECT ... LOCK IN SHAREMODE语句来读取这些记录),但是不能获取这些记录的X锁(比如使用SELECT ... FOR UPDATE语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的S锁释放掉。
开启一个事务,并给查询记录加上共享(S)锁:
开启另一个事务,查询相同记录,也加上共享(S)锁:给查询记录加锁成功
再开启一个事务,查询记录加上排它(X)锁:当前数据集上已经存在共享(S)锁,加排它(X)锁时发送阻塞,给查询记录加锁失败。
阻塞过久会报超时异常,异常信息:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
- SELECT语句读取记录加X锁
select ... for update;
在普通的 SELECT 语句后边加 FOR UPDATE,如果当前事务执行了该语句,那么它会为读取到的记录加排它(X)锁,这样既不允许别的事务获取这些记录的共享(S)锁(比方说别的事务使用SELECT ... LOCK IN SHARE MODE语句来读取这些记录),也不允许获取这些记录的X锁(比如使用SELECT ... FOR UPDATE语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的s锁或者X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的X锁释放掉。
开启一个事务,并给查询记录加上排它(X)锁:
再开启一个事务,给查询记录加上S锁或者X锁:加锁都失败,因为当前记录上已经存在X锁,会阻塞加其他的锁
1.3、锁定写操作
写的操作可以分为 DELETE删除,UPDATE修改 和 INSERT插入 三种。
DELETE 操作锁定
对一条记录做DELETE操作的过程其实是先在B+树中定位到这条记录的位置,然后获取这条记录的X锁,再执行delete mark 操作。我们也可以把这个定位待删除记录在B+树中位置的过程看成是一个获取X锁的锁定读。
UPDATE 操作锁定
情况1:未修改该记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化。则先在B+树中定位到这条记录的位置,然后给这记录加上X锁,最后在原记录的位置进行修改操作。我们也可以把这个定位待修改记录在B+树中位置的过程看成是一个获取X锁的锁定读。
情况2:未修改该记录的键值,并且至少有一个被更新的列占用的存储空间在修改前后发生变化。则先在B+树中定位到这条记录的位置,然后给这记录加上X锁,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在B+树中位置的过程看成是一个获取×锁的锁定读,新插入的记录由INSERT操作提供的隐式锁进行保护。
情况3:修改了该记录的键值,则相当于在原记录上做DELETE操作之后再来一次INSERT操作,加锁操作就需要按照DELETE和INSERT的规则进行了。
INSERT 操作锁定
一般情况下,新插入一条记录的操作并不加锁,通过一种称之为==隐式锁==的结构来保护这条新插入的记录在本事务提交前不被其他事务访问到。