学习资料
【MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!-哔哩哔哩】
【阿里巴巴Java开发手册】https://www.w3cschool.cn/alibaba_java
锁的不同角度分类
锁的分类图如下
从数据操作的类型划分:读锁、写锁
对于数据库中并发事务
读-读
情况并不会引起什么问题。对于写-写
、读-写
或写-读
这些情况可能会引起一些问题,需要使用MVCC
或者加锁的方式来解决它们。在使用加锁
的方式解决问题时,由于既要允许读一读
情况不受影响,又要使写-写
、读-写
或写-读
情况中的操作相互阻塞
,所以MySQL实现一个由两种类型的锁组成的锁系统来解决。这两种类型的锁通常被称为共享锁(Shared Lock S Lock)
和排他锁(Exclusive Lock,X Lock)
,也叫读锁(read lock)
和写锁(write lock)
。
读锁
读锁
:也称为共享锁
、英文用S
表示。针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞的。
写锁
写锁
:也称为排他锁
、英文用X
表示。当前写操作没有完成前,它会阻断其他写锁和读锁。这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源。
需要注意的是对于InnoDB引擎来说,读锁和写锁可以加在表上,也可以加在行上。
锁定读
在采用
加锁
方式解决脏读、不可重复读、幻读
这些问题时,读取一条记录时需要获取该记录的S锁
,其实是不严谨的,有时候需要在读取记录时就获取记录的X锁
,来禁止别的事务读写该记录,为MySQL提供了两种比较特殊的SELECT
语句格式:
对读取的记录加
S锁
:
SELECT ... LOCK IN SHARE MODE;
# 或
SELECT ... FOR SHARE; #(8.0新增语法)
在普通的SELECT语句后边加
LOCK IN SHARE MODE
,如果当前事务执行了该语句,那么它会为读取到的记录加S锁
,这样允许别的事务继续获取这些记录的S锁
(比方说别的事务也使用SELECT ... LOCK IN SHARE MODE
语句来读取这些记录),但是不能获取这些记录的X锁
(比如使用SELECT ... FOR UPDATE
语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的X锁
,那么它们会阻塞,直到当前事务提交之后将这些记录上的S锁
释放掉。
对读取的记录加
X锁
:
SELECT ... FOR UPDATE;
在普通的SELECT语句后边加
FOR UPDATE
,如果当前事务执行了该语句,那么它会为读取到的记录加X锁
,这样既不允许别的事务获取这些记录的S锁
(比方说别的事务使用SELECT ... LOCK IN SHARE MODE
语句来读取这些记录),也不允许获取这些记录的X锁
(比如使用SELECT ... FOR UPDATE
语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的S锁
或者X锁
,那么它们会阻塞,直到当前事务提交之后将这些记录上的X锁
释放掉。
MySQL8.0新特性
在5.7及之前的版本,SELECT … FOR UPDATE,如果获取不到锁,会一直等待,直到
innodb_lock_wait_timeout
超时。在8.0版本中,SELECT … FOR UPDATE,SELECT … FOR SHARE添加NOWAIT、SKIP、LOCKED
语法,跳过锁等待,或者跳过锁定。
通过添加NOWAIT、SKIP、LOCKED语法,能够立即返回。如果查询的行已经加锁:
那么NOWAIT会立即报错返回。
而SKIP LOCKED也会立即返回,只是返回的结果中不包含被锁定的行。
写操作
平常所用到的
写操作
无非是DELETE、UPDATE、INSERT
这三种:
DELETE
:对一条记录做DELETE操作的过程其实是先在B+树
中定位这条记录的位置,然后获取这条记录的X锁
,再执行delete mark
操作。我们也可以把这个定位待删除记录在B+树中的过程看成是一个获取X锁
的锁定读
。
UPDATE
:在对一条记录做UPDATE操作时分为三种情况:
情况1:未修改记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化。
则现在B+树
中定位到这条记录的位置,然后再获取一下记录的X锁
,最后原纪录的位置进行修改操作。我们也可以把这个定位待修改记录在B+树
中位置的过程看成是一个获取X锁
的锁定读
。
情况2:未修改该记录的键值,并且至少有一个被更新的列占用的存储空间在修改前后发生变化。
则先在B+树
中定位到这条记录的位置,然后获取了一下记录的X锁
,将记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新纪录。这个定位待修改记录在B+树
中位置的过程看成是一个获取X锁
的锁定读
,新插入的记录由INSERT
操作提供的隐式锁
进行保护。
情况3:修改了该记录的键值,则相当于在原记录上做
DELETE
操作之后再来一次INSERT
操作,加锁操作就需要按照DELETE
和INSERT
的规则进行了。
INSERT
:一般情况下,新插入一条记录的操作并不加锁,通过一种称之为隐式锁
的结构来保护这条新插入的记录在本事务提交前不被别的事务访问。