读-读
状况:即并发事务相继读取相同的记录
写-写
状况:即并发事务相继对相同的记录作出改动
锁
来实现的。这个所谓的锁
实际上是一个内存中的结构,在事务执行前原本是没有锁的,也就是说一开始是没有锁结构
和记录进行关联的读-写
或写-读
状况:也就是一个事务进行读取操做,另外一个进行改动操做
脏读
、不可重复读
、幻读
的问题加锁
加锁
的方案MVCC
进行的读取操做称之为一致性读
,或者一致性性锁读
,有的地也称之为快照读
。全部普通的SELECT
语句( plain SELECT )在READ COMMITTED
、REPEATABLE READ
隔离级别下都算是一致性读
,一致性读
并不会对表中的任何记录作加锁
操做,其余事务能够自由的对表中的记录作改动并发事务的
读-读
状况并不会引发什么问题,不过对于写-写
、读-写
或写-读
这些状况可能会引发一些问题,须要使用MVCC
或者加锁
的方式来解决它们。在使用加锁
的方式解决问题时,因为既要容许读-读
状况不受影响,又要使写-写
、读-写
或写-读
状况中的操做相互阻塞并发
共享锁和独占锁指针
Shared Locks
,简称S锁
。在事务要读取一条记录时,须要先获取该记录的S锁
排他锁
,英文名:Exclusive Locks
,简称X锁
。在事务要改动一条记录时,须要先获取该记录的X锁
T1
首先获取了一条记录的S锁
以后,事务T2
接着也要访问这条记录:
T2
想要再获取一个记录的S锁
,那么事务T2
也会得到该锁,也就意味着事务T1
和T2
在该记录上同时持有S锁
T2
想要再获取一个记录的X锁
,那么此操做会被阻塞,直到事务T1
提交以后将S锁
释放掉T1
首先获取了一条记录的X锁
以后,那么无论事务T2
接着想获取该记录的S锁
仍是X锁
都会被阻塞,直到事务T1
提交S锁
和S锁
是兼容的,S锁
和X锁
是不兼容的,X锁
和X锁
也是不兼容的锁定读的语句code
加锁
方式解决脏读
、不可重复读
、幻读
这些问题时,读取一条记录时须要获取该下该记录的S锁
,其实这是不严谨的,有时候想在读取记录时就获取记录的X锁
,来禁用别的事务读写该记录
S锁
:
SELECT
语句后边加,若是当前事务执行了该语句,那么它会为读取到的记录加S锁
,这样容许别的事务继续获取这些记录的S锁
(比方说别的事务也使用语句来读取这些记录),可是不能获取这些记录的X锁
(比方说使SELECT ... FOR UPDATE
语句来读取这些记录,或者直接修改这些记录)。若是别的事务想要获取这些记录的X锁
,那么它们会阻塞,直到当前事务提交以后将这些记录上的S锁
释放掉X锁
:
SELECT ... FOR UPDATE;
也就是在普通的SELECT
语句后边加FOR UPDATE
,若是当前事务执行了该语句,那么它会为读取到的记录加X锁
,这样既不容许别的事务获取这些记录的S锁
(比方说别的事务使用语句来读取这些记录),也不容许获取这些记录的X锁
(比方说使用SELECT ... FOR UPDATE
语句来读取这些记录,或者直接修改这些记录)。若是别的事务想要获取这些记录的S锁
或者X锁
,那么它们会阻塞,直到当前事务提交以后将这些记录上的X锁
释放掉写操做索引
写操做
是DELETE
、UPDATE
、INSERT
这三种:
DELETE
操做的过程实际上是先在B+
树中定位到这条记录的位置,而后获取一下这条记录的X锁
,而后再执行delete mark
操做。咱们也能够把这个定位待删除记录在B+
树中位置的过程当作是一个获取X锁
的锁定读UPDATE
操做时分为三种状况:
B+
树中定位到这条记录的位置,而后再获取一下记录的X锁
,最后在原记录的位置进行修改操做。其实咱们也能够把这个定位待修改记录在B+
树中位置的过程当作是一个获取X锁
的锁定读
B+
树中定位到这条记录的位置,而后获取一下记录的X锁
,将该记录完全删除掉(就是把记录完全移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在B+
树中位置的过程当作是一个获取X锁
的锁定读
,新插入的记录由 INSERT
操做提供的隐式锁
进行保护DELETE
操做以后再来一次INSERT
操做,加锁操做就须要按照DELETE
和INSERT
的规则进行了隐式锁
来保护这条新插入的记录在本事务提交前不被别的事务访问前边提到的
锁
都是针对记录的,也能够被称之为行级锁
或者行锁
,对一条记录加锁影响的也只是这条记录而已,咱们就说这个锁的粒度比较细;其实一个事务也能够在表级别进行加锁,天然就被称之为表级锁
或者表锁
,对一个表加锁影响整个表中的记录,咱们就说这个锁的粒度比较粗。给表加的锁也能够分为共享锁
(S锁
)和独占锁
(X锁
)事务
S锁
:
S锁
,那么:
S锁
S锁
X锁
X锁
X锁
:
X锁
(意味着该事务要独占这个表),那么:
S锁
S锁
X锁
X锁
S锁
、X锁
SELECT
、INSERT
、DELETE
、UPDATE
语句时,InnoDB
存储引擎是不会为这个表添加表级别的S锁
或者X锁
的IS锁
、IX锁
InnoDB
存储引擎的表的某些记录加S锁
以前,那就须要先在表级别加一个IS锁
,当咱们在对使用InnoDB
存储引擎的表的某些记录加X锁
以前,那就须要先在表级别加一个IX锁
。IS锁
和IX锁
的使命只是为了后续在加表级别的S锁
和X锁
时判断表中是否有已经被加锁的记录,以免用遍历的方式来查看表中有没有上锁的记录AUTO-INC锁
MySQL
过程当中,咱们能够为表的某个列添加AUTO_INCREMENT
属性,以后在插入记录时,能够不指定该列的值,系统会自动为它赋上递增的值行锁
,也称为记录锁
,顾名思义就是在记录上加的锁行锁
类型
Record Locks
:有S锁
和X锁
之分,当一个事务获取了一条记录的S型记录锁
后,其余事务也能够继续获取该记录的S型记录锁
,但不能够继续获取X型记录锁
;当一个事务获取了一条记录的X型记录锁
后,其余事务既不能够继续获取该记录的S型记录锁
,也不能够继续获取X型记录锁
Gap Locks
:能够用于解决可重复读级别下的幻读问题,防止插入幻影记录Next-Key Locks
: 锁住某条记录同时又能够阻止其余事务在该条记录前边的间隙
插入新纪录,其是Record Locks
和gap Locks
的合体它既能保护该条记录,又能阻止别的事务将新记录插入被保护记录前边的间隙
Insert Intention Locks
:一个事务在插入一条记录时须要判断一下插入位置是否是被别的事务加了所谓的gap锁
,若存在,插入操做须要等待,直到拥有的gap锁
对应的事务提交,事务在等待的时候也须要在内存中生成一个锁结构
,代表有事务想在某个间隙
中插入新纪录,可是如今在等待。这种类型的锁命名为Insert Intention Locks
,也成为插入意向锁
隐式锁
:一个事务在执行INSERT
操做时,若是即将插入的间隙已经被其余事务加了gap
锁,那么本次INSERT
操做会阻塞,而且当前事务会在该间隙上加一个插入意向锁
,不然通常状况下INSERT
操做是不加锁的锁结构
与之关联
锁结构
中的状况
锁所在的事务信息
: 不管是表锁
仍是行锁
,都是在事务执行过程当中生成的,哪一个事务生成了这个锁结构
,这里就记载着这个事务的信息。其本质是一个指针,经过该指针能够找到内存中关于该事务的更多信息索引信息
:对于行锁
来讲,须要记录一下加锁的记录是属于哪一个索引的表锁/行级信息
:
表锁
:记载着这是对哪一个表加的锁,还有其余的一些信息行锁
:记载了三个重要的信息
n_bits
属性表明使用了多少比特位type_mode
:这是一个32位的数,被分红了lock_mode
、lock_type
和rec_lock_type
三个部分
LOCK_IS
(十进制的0
):表示共享意向锁,也就是IS锁
。LOCK_IX
(十进制的1
):表示独占意向锁,也就是IX锁
。LOCK_S
(十进制的2
):表示共享锁,也就是S锁
。LOCK_X
(十进制的3
):表示独占锁,也就是X锁
。LOCK_AUTO_INC
(十进制的4
):表示AUTO-INC锁
。LOCK_TABLE
(十进制的16
),也就是当第5个比特位置为1时,表示表级锁。LOCK_REC
(十进制的32
),也就是当第6个比特位置为1时,表示行级锁rec_lock_type
),使用其他的位来表示。只有在lock_type
的值为LOCK_REC
时,也就是只有在该锁为行级锁时,才会被细分为更多的类型:
LOCK_ORDINARY
(十进制的0
):表示next-key
锁 。LOCK_GAP
(十进制的512
):也就是当第10个比特位置为1时,表示gap锁
。LOCK_REC_NOT_GAP
(十进制的1024
):也就是当第11个比特位置为1时,表示记录锁
。LOCK_INSERT_INTENTION
(十进制的2048
):也就是当第12个比特位置为1时,表示插入意向锁