文章目录
- 一、行锁
- Q:MySQL行锁有哪些使用场景
- Q:MySQL那些命令会导致发生行锁?
- 二、间隙锁(Gap Lock)
- 间隙锁有哪些使用场景
- 三、临建锁(Next-Key Lock)
- 四、表锁
- 意向锁(Intention Lock)
- 作用
- 为什么意向锁是表级锁呢?
- 意向锁怎么支持表锁和行锁并存?
- 如何查看
一、行锁
行级锁是MySQL中的一种锁定机制,它可以对数据库表中的单独一行进行锁定。相比于表级
锁和页锁,行级锁的粒度更小,因此在处理高并发事务时,能提供更好的并发性能和更少的
锁冲突。然而,行级锁也需要更多的内存和CPU资源,因为需要对每一行都进行管理。
在MySQL中,行级锁主要由InnoDB存储引擎提供。InnoDB支持两种类型的行级锁:共享锁
(S锁)和排他锁(X锁)。
- 共享锁(S锁):共享锁也称为读锁,它允许一个事务读取一行数据。当一行数据被共享
锁锁定时,其他事务可以读取这行数据,但不能对其进行修改。 - 排他锁(X锁):排他锁也称为写锁,它允许一个事务读取和修改一行数据。当一行数据
被排他锁锁定时,其他事务不能读取也不能修改这行数据。
在实际使用中,InnoDB还提供了一种名为“间隙锁”(Gap Lock)的特性。间隙锁不仅锁
定一个具体的行,还锁定它前后的“间隙”,即这一行之前的行和之后的行之间的空间。间
隙锁可以防止其他事务插入新的行到已锁定行的前后,从而可以解决一些并发问题。
值得注意的是,行级锁只在事务中有效,也就是说,只有在一个事务开始(BEGIN)后并在
事务提交(COMMIT)或回滚(ROLLBACK)之前,才能对数据行进行锁定。如果在非事务
环境中执行SQL语句,那么InnoDB会在语句执行结束后立即释放所有的锁。
Q:MySQL行锁有哪些使用场景
MySQL中的行级锁(Row Level Locks)通常在以下几种场景中被使用:
- 高并发读写操作:在需要高并发读写操作的场景中,行级锁可以提高性能和并发性,因为
它允许多个事务并发地操作不同的行。 - 单行操作:对于需要操作单行数据的SQL语句(例如基于主键或者唯一索引的UPDATE、
DELETE和INSERT语句),行级锁可以提供较好的并发性和性能。 - 短期锁:在需要对数据行进行短时间锁定的情况下,行级锁可以防止长时间阻塞其他事
务。 - 实现并发控制:在需要确保数据一致性和隔离性的事务中,行级锁是实现并发控制的重要
机制。 - 复杂的事务处理:在需要对多行数据进行复杂处理的事务中,可以使用行级锁来锁定这些
行,防止在事务处理过程中数据被其他事务修改。
使用行级锁需要注意,由于行级锁的锁定粒度较小,它可能会消耗更多的系统资源(例如内
存和CPU),特别是在处理大量数据时。此外,使用行级锁也可能导致死锁,需要使用合适
的策略来避免死锁,例如在事务中按照一定的顺序锁定行。
Q:MySQL那些命令会导致发生行锁?
在MySQL中,主要是InnoDB存储引擎提供了行级锁(Row Level Locking)。一般来说,以
下这些类型的操作会导致InnoDB对数据行进行加锁:
- SELECT … FOR UPDATE:这种查询会对选定的行添加一个排他锁(X锁),这意味着
其他事务不能修改这些行,也不能对这些行添加共享锁。 - SELECT … LOCK IN SHARE MODE:这种查询会对选定的行添加一个共享锁(S
锁),这意味着其他事务不能修改这些行,但可以对这些行添加共享锁。 - INSERT:插入操作会对新添加的行添加一个排他锁(X锁)。
- UPDATE:更新操作会对被更新的行添加一个排他锁(X锁)。
- DELETE:删除操作会对被删除的行添加一个排他锁(X锁)。
这些加锁操作都是在事务中进行的,即只有在事务开始(BEGIN)后并在事务提交
(COMMIT)或回滚(ROLLBACK)之前,才会对数据行进行加锁。如果在非事务环境中执
行上述SQL语句,那么InnoDB会在语句执行结束后立即释放所有的锁。
请注意,加锁的粒度和范围取决于WHERE子句中用到的索引。如果WHERE子句中用到了唯
一索引(例如主键索引),那么InnoDB只会锁定匹配的行。如果没有用到唯一索引,那么
InnoDB可能会锁定更多的行,甚至是整个表,这就可能导致锁冲突和性能问题。
此外,InnoDB还支持间隙锁(Gap Locks)和临键锁(Next-Key Locks),这两种锁都可以
在某些情况下提供更好的并发控制。
二、间隙锁(Gap Lock)
间隙锁(Gap Locks)是MySQL InnoDB存储引擎提供的一种锁定机制。它锁定的不是具体
的行记录,而是两个索引之间的间隙(或者说区间),这样可以防止新的记录插入到该间
隙,确保数据的一致性和事务的隔离性。
间隙锁常常与记录锁(Record Locks)一起使用,共同形成Next-Key锁,保护索引记录的范
围查询和扫描操作。
以下是间隙锁的主要类型:
- 区间-区间间隙锁:锁定两个索引键之间的间隙,或者是第一个索引键之前的间隙。
- 区间-记录间隙锁:锁定一个索引键和一个记录之间的间隙。
- 记录-区间间隙锁:锁定一个记录和一个索引键之间的间隙。
间隙锁的存在,主要是为了解决幻读问题。所谓幻读,是指在一个事务内读取某个范围的记
录时,另外一个事务在该范围内插入了新的记录,当第一个事务再次读取该范围的记录时,
会发现有些原本不存在的记录,这就是幻读。
举例来说,假设我们有一个存储学生信息的表,有一个事务A要查询年龄在10-20之间的学
生,它在查询前会对这个区间加锁。此时如果有另一个事务B想要插入一个年龄为15的学生,
由于这个年龄的范围已经被事务A锁定,所以事务B必须等待,直到事务A完成,释放锁。这
样就避免了幻读的产生。
值得注意的是,由于间隙锁会锁定范围,如果并发事务较多且涉及的数据范围有交集,可能
会引发性能问题,甚至死锁。因此,在设计数据库和选择隔离级别时,需要综合考虑数据一
致性和并发性能。
间隙锁有哪些使用场景
间隙锁(Gap Locks)在MySQL数据库的InnoDB存储引擎中主要用于以下场景:
- 防止幻读:间隙锁的主要目的是防止其他事务在已经锁定的范围内插入新的行。这可以避
免"幻读"问题,即一个事务在读取某个范围内的所有行时,另一个事务插入了一个新行,
当第一个事务再次读取该范围时,会发现有一个"幻影"行。 - 范围查询:在执行范围查询时,如果事务需要对查询结果进行更新或删除,那么间隙锁可
以保证在事务执行期间,不会有新的行插入到查询范围中。
例如,以下事务会在Orders表的OrderID列值在1到100之间的所有行上设置排他锁,并在这
些行的间隙上设置间隙锁:
START TRANSACTION;
SELECT * FROM Orders WHERE OrderID BETWEEN 1 AND 100 FOR UPDATE;
COMMIT;
- 防止死锁:在某些情况下,间隙锁可以帮助防止死锁。如果没有间隙锁,那么两个事务可
能都会试图在同一位置插入一个新行,导致彼此等待对方释放锁,从而形成死锁。
需要注意的是,间隙锁在可重复读(REPEATABLE READ)和序列化(SERIALIZABLE)这两
个隔离级别下才会使用,在读已提交(READ COMMITTED)和读未提交(READ
UNCOMMITTED)这两个隔离级别下,InnoDB不会使用间隙锁。
可以看到将5–11这些数据都锁住了,这种情况下其它的事务想要去在这个1到11的id区间内做数据修改是会被阻塞的,知道我们将事务提交或者回滚将锁释放才行
三、临建锁(Next-Key Lock)
Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法。通过临建锁可以解决幻读的问题。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
四、表锁
意向锁(Intention Lock)
意向锁是表锁,为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。
作用
意向锁的作用是告诉其他事务,当前事务要获取某个页的行锁,以避免其他事务误判并尝试获取该页的表锁。意向锁的引入可以提高并发性能,减少锁冲突。
为什么意向锁是表级锁呢?
当我们需要加一个排他锁时,需要根据意向锁去判断表中有没有数据行被锁定(行锁);
(1)如果意向锁是行锁,则需要遍历每一行数据去确认;
(2)如果意向锁是表锁,则只需要判断一次即可知道有没数据行被锁定,提升性能。
意向锁怎么支持表锁和行锁并存?
(1)首先明确并存的概念是指数据库同时支持表、行锁,而不是任何情况都支持一个表中同
时有一个事务A持有行锁、又有一个事务B持有表锁,因为表一旦被上了一个表级的写锁,肯
定不能再上一个行级的锁。
(2)如果事务A对某一行上锁,其他事务就不可能修改这一行。这与“事务B锁住整个表就
能修改表中的任意一行”形成了冲突。所以,没有意向锁的时候,让行锁与表锁共存,就会
带来很多问题。于是有了意向锁的出现,如前面所言,数据库不需要在检查每一行数据是否
有锁,而是直接判断一次意向锁是否存在即可,能提升很多性能。
如何查看
当我执行行锁的时候会为这个表添加一个意向锁,这个意向锁是什么类型取决于你为行锁添加的是什么类型
我这里是要给意向排他锁,是因为我行锁用的是排他锁。