mysql-锁

一、基础概念

锁是数据库系统区分与文件系统的一个关键特性。

为了保证数据一致性,必须有锁的介入。

数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性。

mysql锁主要是为了解决并发写数据时的一种安全机制。

lock与latch

这里还要区分锁中容易令人混淆的概念lock与 latch。在数据库中,lock与 latch都可以被称为“锁”。但是两者有着截然不同的含义,本章主要关注的是lock。

latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在 InnoDB存储引擎中, latch又可以分为 mutex(互斥量)和 rwlock(读写锁)。

其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。


lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。

并且一般lock的对象仅在事务 commit或 rollback后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock,正如在大多数数据库中一样,是有死锁机制的。下表显示了lock与latch的不同。

mysql-锁_第1张图片

1.1 锁的类型

基于锁的属性分类:共享锁、排他锁。

基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎 )

基于锁的状态分类:意向共享锁、意向排它锁

1.共享锁与排它锁


Innodb存储引擎实现了如下2种标准的行级锁

共享锁(S lock),允许事务读取一行数据。

排它锁(X lock),允许事务删除或者更新一行数据。


当一个事务获取了行r的共享锁,那么另外一个事务也可以立即获取行r的共享锁,因为读取并未改变行r的数据,这种情况就是锁兼容。

但是如果有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁。

mysql-锁_第2张图片

简单的说,就是一个事务锁住了某行,其他事务可以读取该行,但不能做修改和删除等操作。


2.意向共享锁(IS Lock)和意向排它锁(IX Lock)

此外, InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。

为了支持在不同粒度上进行加锁操作, InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention lock)。

意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁,如下图所示。

mysql-锁_第3张图片

若将上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。

例如图6-3,如果需要对页上的记录r进行上X锁,那么分别需要对数据库A、表、页上意向锁IX,最后对记录r上ⅹ锁。

若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。

举例来说,在对记录r加ⅹ锁之前,已经有事务对表1进行了S表锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加上IX,由于不兼容,所以该事务需要等待表锁操作的完成。


InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:

  1. 意向共享锁(IS Lock),事务想要获得一个表中某几行的共享锁。
  2. 意向排它锁(IX Lock),事务想要获得一个表中某几行的排它锁。

由于 InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。故表级意向锁与行级锁的兼容性如下表所示。

mysql-锁_第4张图片

思考

1.MySQL SELECT … FOR UPDATE 的Row Lock 与Table Lock

上面介绍过SELECT … FOR UPDATE 的用法,不过锁定(Lock)的数据是判别就得要注意一下了。对于主键或唯一索引,则上记录锁。其他索引上间隙锁,没有索引列将锁表

2.锁表的场景

查询到一部分结果后,然后对这些结果进行更新。如果更新的条件不是索引项,将锁表,那么其他线程在读写这张表时将会线程阻塞。

如移动端读取了某些消息后,又将这些消息置为已读,而这项操作是在一个事务上。

解决办法:更新或者删除等where条件尽量避免锁表,条件上尽量使用唯一索引或者主键索引。即查询结果从Db中反查一下获取到主键再进行更新。

3.select…for update和lock in share mode对比

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这种显示加锁的查询操作。

1.2 行锁的三种算法

record lock,记录锁,单个行记录的锁
Gap Lock,间隙锁,锁定一个范围,但不包含记录本身,遵循左开右闭原则。
Next_key Lock:Gap Lock+ Record Lock,锁定一个范围,并且锁定记录本身。InnoDB默认加锁方式是Next_key Lock

注意:InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁或者间隙锁,否则,InnoDB将使用表锁。

1.间隙锁

什么是间隙锁?

间隙锁是一个在索引记录之间的间隙上的锁。

间隙的范围

根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。

mysql-锁_第5张图片

select * from t where number=6;那么间隙锁锁定的间隙为:(5,11),所以你再想插入,更新,删除5到11之间的数就会被阻塞。

间隙锁的作用

阻止多个事务将纪录插入到同一个范围内,保证某个间隙内的数据在锁定情况下不会发生任何变化。可以解决Phantom Problem(幻读)

用户可以通过以下两种方式来显式地关闭Gap Lock:

  • 将事务的隔离级别设置为 READ COMMITTED
  • 将参数 innodb_locks_unsafe_for_binlog设置为1

最后需再次提醒的是,对于唯一键值的锁定, Next-Key Lock降级为Record Lock仅存在于查询所有的唯一索引列。

若唯一索引由多个列组成,而查询仅是查找多个唯一索引列中的其中一个,那么查询其实是range类型查询,而不是 point类型查询,故InnoDB存储引擎依然使用 Next-Key Lock进行锁定。


1.3 数据库死锁

1.AB-BA死锁

mysql-锁_第6张图片


@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

你可能感兴趣的:(mysql,mysql,数据库,java)