在InnoDB中,锁可以分为两种级别,一种是共享锁(S锁),另一种是排他锁(X锁)。
共享锁&排他锁
共享锁又称为读锁,由读取操作创建。其他用户可以并发读取数据,但直到所有共享锁都被释放之前,任何事务都无法对数据进行修改(获得数据上的排他锁)。
如果事务T对数据A加上共享锁后,其他事务只能对A再加共享锁,而不能加排他锁。获得共享锁的事务只能读取数据,而不能修改数据。
代码语言:plsql复制SELECT ... LOCK IN SHARE MODE;
在查询语句后添加LOCK IN SHARE MODE
,MySQL会为查询结果中的每行加上共享锁。只有当没有其他线程对查询结果集中的任意行使用排他锁时,才能成功获取共享锁;否则将被阻塞。其他线程可以读取已经被加了共享锁的表,且这些线程将读取相同版本的数据。
排他锁又称为写锁,一旦事务T对数据A加上排他锁,其他事务就无法再对A加任何类型的锁。获得排他锁的事务既可读取数据,又可修改数据。
代码语言:plsql复制SELECT ... FOR UPDATE;
除了S锁和X锁之外,InnoDB还有另外两种锁,分别是IX锁和IS锁,这里的"I"代表着"Intention",即意向锁。IX即意向排他锁,IS即意向共享锁。
在查询语句后添加FOR UPDATE
,MySQL会对查询命中的每条记录都加排他锁(如果有索引,则通过索引加锁;如果没有索引,则会锁定整个表)。只有当没有其他线程对查询结果集中的任何一行使用排他锁时,才能成功申请排他锁;否则将被阻塞。
意向锁
在MySQL的InnoDB引擎中,支持多种锁级别,包括行级锁和表级锁。当多个事务需要访问共享资源时,如果每个事务都直接请求锁,可能会导致彼此相互阻塞,甚至引发死锁。
举个例子:
事务A对表Table1中的某一行加上了行级锁,这导致该行只能读取而不能修改。与此同时,事务B试图申请对Table1的表级锁。如果事务B成功获取表级锁,那么它就能修改表中的任意一行记录,从而引发冲突。
为解决这一问题,事务B在申请Table1的表级锁时,需要先检查是否有其他事务已经加了行级锁。然而,事务B无法简单地遍历表中所有数据逐行判断是否已被锁定,这样效率太低了。
为了解决这一问题,MySQL引入了意向锁机制。意向锁作为一种锁机制,在数据库管理系统中旨在协调不同锁粒度(如行级锁和表级锁)之间的并发问题。(对于同一锁粒度内的并发问题,如多个行级锁之间的冲突,则通过行级互斥锁来解决。)
注意:
- 意向锁并非直接锁定资源,而是用于通知其他事务,以防止它们在相同资源上设置不兼容的锁。
- 意向锁不是由用户直接请求的,而是由MySQL系统管理的。
当一个事务请求获取行级锁或表级锁时,MySQL会自动获取相应表的意向锁。这样一来,其他事务在请求表锁时,可以先通过该意向锁探知是否有已经加锁,并根据意向锁的类型(意向共享锁/意向排它锁)判断自身是否可获取锁。这种方式在不阻塞其他事务的情况下,为当前事务锁定资源。
意向锁有两种类型:意向共享锁和意向排它锁。
- 意向共享锁:代表事务打算对资源设置共享锁(读锁)。通常用于暗示事务打算读取资源,不希望在读取时有其他事务设置排它锁。
- 意向排它锁:代表事务打算对资源设置排它锁(写锁)。这表明事务计划修改资源,不希望其他事务同时设置共享或排它锁。
意向锁是表级锁,在触发意向锁的事务提交或回滚后会释放。
以下是MySQL官网上给出的这几种锁之间的冲突关系:
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
记录锁
记录锁(Record Lock)是一种加在索引记录上的锁,用于保护特定行数据的完整性。例如,对于语句 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
,将会对满足条件 c1=10 的记录加锁,以防止其他事务对该行进行插入、更新或删除操作。
尽管记录锁通常被称为行级锁,但需要特别注意的是,它实际上锁定的是索引记录而非数据行本身。此外,记录锁仅限于锁定索引。
当表中不存在索引时该如何处理?InnoDB 引擎会自动创建一个隐藏的聚簇索引,并使用该索引进行记录锁定。
若表中未定义主键,MySQL会默认选择一个唯一的非空索引作为聚簇索引。若不存在适用的非空唯一索引,则会创建一个隐藏的主键(row_id)作为聚簇索引。
关于记录锁的加锁原则。感兴趣的小伙伴一键三连。后续可以出一片文章。
插入记录锁
插入意向锁是一种由插入操作在行插入之前设置的间隙锁。这种锁表明了插入的意图,以这样一种方式,如果多个事务尝试插入到同一索引间隙但不在间隙内的相同位置,则它们不需要相互等待。
举例来说,假设存在索引记录的值为4和7。当不同事务分别尝试插入值为5和6时,它们会在获取插入行的独占锁之前,各自使用插入意向锁锁定4和7之间的间隙。由于它们插入的行并不冲突,因此它们不会相互阻塞。然而,如果它们都试图插入6,那么就会发生阻塞情况。
AUTO-INC 锁
AUTO-INC 锁是一种特殊的表级锁,由向包含 AUTO_INCREMENT 列的表插入数据的事务所获取。在最简单的情况下,如果一个事务正在向表中插入值,其他任何事务都必须等待,以便执行它们自己的插入操作,这样第一个事务插入的行就会接收到连续的主键值。
innodb_autoinc_lock_mode 变量控制用于自增锁定的算法。它允许你在可预测的自增值序列和插入操作的最大并发性之间进行权衡。
在MySQL 5.1之前,AUTO-INC锁是一种表级锁。
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!