文章目录
😊 @ 作者:Lion J
`💖 @ 主页:
https://blog.csdn.net/weixin_69252724`🎉 @ 主题: MySQL__锁)
⏱️ @ 创作时间:2024年04月27日
————————————————
什么是MySQL的锁?
锁是一种常见的并发事务的控制方式。
表锁与行锁有什么区别
- 表级锁: MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
- 行级锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。
1. 什么叫做针对索引字段加锁?
针对索引字段加锁是指在执行 SQL 操作时,数据库会根据索引来对数据行进行加锁,而不是对整个表进行加锁。这样的锁定方式被称为行级锁,因为它仅锁定正在操作的行,而不是整个表。
2. 如果查询不走索引字段, 那还怎么加锁, 直接加表锁吗?
别说,还真是. 如果不走索引, 那他查询时候不知道给哪一行数据加锁, 就只能给表加锁了. 此时就根据操作语句来看是加什么锁, 看是共享锁还是独占锁. InnoDB存储引擎一般对于普通的查询语句来说都是加共享锁
- 当查询字段是索引字段时,MySQL 可以直接利用索引进行定位,并且只需要锁定索引对应的行,而不需要锁定整个表或者额外的间隙。这样可以最大程度地减少锁定的范围,提高并发性能,同时确保了事务的隔离性和一致性。
- 当查询字段不是索引字段时,MySQL 需要进行全表扫描或者范围扫描,无法直接利用索引定位到符合条件的行。在这种情况下,为了保证查询结果的一致性,MySQL 可能会使用表级锁定或者间隙锁,以确保事务之间不会出现干扰或者幻读现象。这会增加锁定的范围,降低并发性能,但能保证数据的完整性。
行级锁使用注意什么?
InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 UPDATE、DELETE 语句时,如果 WHERE条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。这个在我们日常工作开发中经常会遇到,一定要多多注意!!!
InnoDB 有哪几类行锁?
- 记录锁:也被称为记录锁,属于单个行记录上的锁。
- 间隙锁:锁定一个范围,不包括记录本身。
- 临键锁:临键锁是查询时InnoDB根据查询的条件而锁定的一个范围,这个范围中包含有间隙锁和记录锁;临键锁=间隙锁+记录锁。
其设计的目的是为了解决Phantom Problem(幻读);主要是阻塞insert,但由于临键锁中包含有记录锁,因此临键锁所锁定的范围内如果包含有记录,那么也会给这些记录添加记录锁,从而造成阻塞除insert之外的操作;
间隙锁 和 临表锁的使用?
在默认情况下, InnoDB在RR的事务级别下, 使用间隙锁和索引扫描来防止幻读的情况, 在默认的隔离级别 RR下,行锁默认使用的是 Next-Key Lock(临键锁)。
- 如果操作的索引是唯一索引或主键, 数据存在情况
就会将临键锁降级为记录所只对操作的数据进行加锁, 仅仅是 锁住索引本身,而不是范围。
- 如果Sql语句的条件判断字段如果为唯一索引,且给不存在的记录加锁时,行锁会优化为间隙锁给前后间隙加锁。例如:
记录3-8, 已经被事务1锁住了导,导致事务2inset不了
- 如果Sql语句的条件判断字段如果为非唯一索引, 那么就会给此数据, 以及此数据前后的数据加上间隙锁
比如查询:
SELECT * FROM cum WHERE age = 6 LOCK IN SHARE MODE,那么id为(1,6],[6,10)的数据会加锁。
- 范围查询的Sql语句的条件判断字段如果为唯一索引,会将此行数据和后面的全部间隙加上锁。例如:
比如执行:
SELECT * FROM cum WHERE id >= 10 LOCK IN SHARE MODE,那么会将[10,无穷大)加锁。
注意:间隙锁唯一目的是防止其他事务将数据插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
对于行锁(记录锁, 间隙锁, 临表锁)都是区分一个共享锁和排他锁的
共享锁与排他锁?
不论是表级锁还是行级锁,都存在共享锁(S 锁)和排他锁(X 锁)这两类:
- 共享锁(S 锁):又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取。
- 排他锁(X 锁):又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁。
排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。
由于 MVCC 的存在,对于一般的 SELECT 语句,InnoDB 不会加任何锁, 都是使用 一致性非锁定读的方式来加共享锁方式
不过, 你可以通过以下语句显式加共享锁或排他锁。
# 共享锁
SELECT ... FOR SHARE;
# 排他锁
SELECT ... FOR UPDATE;
什么是意向锁?
如果需要用到表锁的话,如何判断表中的记录没有行锁呢,一行一行遍历肯定是不行,性能太差。
我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。
意向锁是表级锁,共有两种:
- 意向共享锁 :事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 意向共享锁。
- 意向排他锁:事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的意向排他锁。
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
这种机制可以帮助快速判断表中的记录是否被锁定,从而优化锁定的策略,减少不必要的等待时间和资源浪费。
当前读与快照读区别?
快照读(一致性非锁定读)
就是单纯的 SELECT 语句,但不包括下面这两类 SELECT 语句:
# 排他
SELECT ... FOR UPDATE
# 共享锁
SELECT ... LOCK IN SHARE MODE;
# 共享锁
SELECT ... FOR SHARE;
快照即记录的历史版本,每行记录可能存在多个历史版本(这是数据库的多版本技术)。
快照读的情况下,如果读取的记录正在执行 UPDATE/DELETE 操作,读取操作不会因此去等待记录上 X 锁的释放,而是会去读取行的一个快照。
只有在事务隔离级别 RC(读取已提交) 和 RR(可重读)下,InnoDB 才会使用一致性非锁定读:
- 在 RC 级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据。这也就是为什么会导致不可重复读的原因
- 在 RR 级别下,对于快照数据,一致性非锁定读总是读取本事务开始时的行数据版本。快照读比较适合对于数据一致性要求不是特别高且追求极致性能的业务场景。
当前读 (一致性锁定读)
就是给行记录加 X 锁或 S 锁。当前读的常用语句如下
# 加一个X锁
SELECT...FOR UPDATE
# 加一个S锁
SELECT...LOCK IN SHARE MODE
# 加一个S锁
SELECT...FOR SHARE
# 加一个X锁
INSERT...
UPDATE...
DELETE...