锁是数据库系统区分与文件系统的一个关键特性。
为了保证数据一致性,必须有锁的介入。
数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性。
mysql锁主要是为了解决并发写数据时的一种安全机制。
这里还要区分锁中容易令人混淆的概念lock与 latch。在数据库中,lock与 latch都可以被称为“锁”。但是两者有着截然不同的含义,本章主要关注的是lock。
latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在 InnoDB存储引擎中, latch又可以分为 mutex(互斥量)和 rwlock(读写锁)。
其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。
并且一般lock的对象仅在事务 commit或 rollback后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock,正如在大多数数据库中一样,是有死锁机制的。下表显示了lock与latch的不同。
基于锁的属性分类:共享锁、排他锁。
基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎 )
基于锁的状态分类:意向共享锁、意向排它锁
Innodb存储引擎实现了如下2种标准的行级锁
共享锁(S lock),允许事务读取一行数据。
排它锁(X lock),允许事务删除或者更新一行数据。
当一个事务获取了行r的共享锁,那么另外一个事务也可以立即获取行r的共享锁,因为读取并未改变行r的数据,这种情况就是锁兼容。
但是如果有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁。
简单的说,就是一个事务锁住了某行,其他事务可以读取该行,但不能做修改和删除等操作。
此外, InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。
为了支持在不同粒度上进行加锁操作, InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention lock)。
意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁,如下图所示。
若将上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。
例如图6-3,如果需要对页上的记录r进行上X锁,那么分别需要对数据库A、表、页上意向锁IX,最后对记录r上ⅹ锁。
若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。
举例来说,在对记录r加ⅹ锁之前,已经有事务对表1进行了S表锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加上IX,由于不兼容,所以该事务需要等待表锁操作的完成。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:
由于 InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。故表级意向锁与行级锁的兼容性如下表所示。
上面介绍过SELECT … FOR UPDATE 的用法,不过锁定(Lock)的数据是判别就得要注意一下了。对于主键或唯一索引,则上记录锁。其他索引上间隙锁,没有索引列将锁表
查询到一部分结果后,然后对这些结果进行更新。如果更新的条件不是索引项,将锁表,那么其他线程在读写这张表时将会线程阻塞。
如移动端读取了某些消息后,又将这些消息置为已读,而这项操作是在一个事务上。
解决办法:更新或者删除等where条件尽量避免锁表,条件上尽量使用唯一索引或者主键索引。即查询结果从Db中反查一下获取到主键再进行更新。
SELECT … LOCK IN SHARE MODE走的是IS锁(意向共享锁)。
即在符合条件的rows上都加了共享锁,这样的话,其他session可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的session执行完成(否则直接锁等待超时)。
SELECT … FOR UPDATE 走的是IX锁(意向排它锁)。
即在符合条件的rows上都加了排它锁,其他session也就无法在这些记录上添加任何的S锁或X锁。
如果不存在一致性非锁定读的话,那么其他session是无法读取和修改这些记录的,但是innodb有非锁定读(快照读并不需要加锁),for update之后并不会阻塞其他session的快照读取操作,除了select …lock in share mode和select … for update这种显示加锁的查询操作。
record lock,记录锁,单个行记录的锁
Gap Lock,间隙锁,锁定一个范围,但不包含记录本身,遵循左开右闭原则。
Next_key Lock:Gap Lock+ Record Lock,锁定一个范围,并且锁定记录本身。InnoDB默认加锁方式是Next_key Lock
注意:InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁或者间隙锁,否则,InnoDB将使用表锁。
1.间隙锁
什么是间隙锁?
间隙锁是一个在索引记录之间的间隙上的锁。
间隙的范围
根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。
select * from t where number=6;那么间隙锁锁定的间隙为:(5,11),所以你再想插入,更新,删除5到11之间的数就会被阻塞。
间隙锁的作用
阻止多个事务将纪录插入到同一个范围内,保证某个间隙内的数据在锁定情况下不会发生任何变化。可以解决Phantom Problem(幻读)
用户可以通过以下两种方式来显式地关闭Gap Lock:
最后需再次提醒的是,对于唯一键值的锁定, Next-Key Lock降级为Record Lock仅存在于查询所有的唯一索引列。
若唯一索引由多个列组成,而查询仅是查找多个唯一索引列中的其中一个,那么查询其实是range类型查询,而不是 point类型查询,故InnoDB存储引擎依然使用 Next-Key Lock进行锁定。
@see 《MySQL技术内幕:InnoDB存储引擎(第2版)》
对于数据库的死锁,数据库将自动进行死锁解决。
常见案例参考
用户A查询一条纪录,然后修改该条纪录;这时用户B先查询这条记录然后修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,
而用户B里的试图修改上升独占锁由于A 有共享锁存在所以必须等A释放掉共享锁,而A由于B的共享锁而无法上升独占锁A也就不可能释放共享锁,于是出现了死锁。
@see https://www.cnblogs.com/sivkun/p/7518540.html
https://blog.csdn.net/zheng0518/article/details/53844720
1.《MySQL技术内幕:InnoDB存储引擎(第2版)》
2.MySQL技术内幕 https://blog.csdn.net/shenchaohao12321/category_8075653.html