MySQL锁都有什么?详解!

独占锁:一般指的行锁

共享锁:

独占表锁:

共享表锁:

意向排他锁:

意向共享锁:

CAS有用到硬件(操作系统)的锁

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

【NOTE : 针对事务才有加锁的意义。】

分类:MySQL中的锁,按照锁的粒度分,分为以下三类:

  1. 全局锁:锁定数据库中的所有表。

  2. 表级锁:每次操作锁住整张表。

  3. 行级锁:每次操作锁住对应的行数据。

全局锁:

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。 其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

DML、DDL阻塞,DQL可以

mysqldump数据库备份工具,不是mysql的命令,应该在windows命令行输入

 flush tables with read lock;    -- 加全局锁
 mysqldump -uroot -p1234 itcat>itcast.sql    -- 数据库备份,不是mysql的命令
 unlock tables;  -- 释放全局锁

特点

数据库中加全局锁,是一个比较重的操作,存在以下问题:

如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。

如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。

在innodb引擎中,我们可以在备份时加上一个参数–single-transaction参数来完成不加锁的一致性数据备份,底层通过快照读实现的

 mysqldump --single-transaction --uroot -p123456 库名 > 指定存放位置/xxx.sql

如果数据库的引擎支持的事务支持可重复读的隔离级别,那么在备份数据库之前先开启事务,会先创建 Read View,然后整个事务执行期间都在用这个 Read View,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作。

因为在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。【不会出现不一致的情况】

–single-transaction 参数的时候,就会在备份数据库之前先开启事务。这种方法只适用于支持「可重复读隔离级别的事务」的存储引擎。

表级锁:

【共享锁、排他锁】只有共享锁与共享锁之间是兼容的

共享锁(读锁):S锁,只可加S锁。

排他锁(写锁):X锁,不可再加锁

此处客户端《 == 》事务,讨论事务对同一数据对象加锁

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。

对于表级锁,主要分为以下三类:表锁、元数据锁、意向锁

表锁:

不是通过select …… in share mode 来加

表锁除了会限制别的线程的读写外,也会限制本线程接下来的读写操作。

【还会再加一个元数据锁?】

【注意!当前会话不能同时对多个表加读锁或者写锁,加了第二个锁之后,第一个锁会自动解除】--

对于表锁,分为两类:

1.表共享读锁(read lock)所有的事务(客户端)都只能读(当前加锁的客户端也只能读,不能写),不能写 。

【当前客户端报错,另一个客户端阻塞】---对于当前会话来说是串行的,阻塞了读锁就释放不了了,所以写会直接失败

【另一个客户端释放锁没用,他有自己的表锁?居然可以自己再加一层锁】----因为共享锁允许多个事务并发地持有相同的锁来读取数据。【S锁可以再加S锁】

2.表独占写锁(write lock),对当前加锁的客户端,可读可写,对于其他的客户端,不可读也不可写。

读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写。

语法:

 加锁:lock tables 表名 read/write
 释放锁:unlock tables / 客户端断开连接

元数据锁

表结构(元数据)

元数据锁(meta data lock,MDL),MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(未提交事务)的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。

在MySQL5.5中引入了MDL,当对一张表进行增删改查CRUD的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。

「读读」并不冲突;

SQL 语句锁类型说明
LOCK TABLES xxx READ / WRITEshared_read_only / shared_no_read_write读写锁和写锁
SELECTSELECT ... LOCK IN SHARE MODE【自动帮我们加上SHARED_READ。lock in share mode 是指共享锁】shared_read(共享锁)与 shared_read 和 shared_write 兼容,与 exclusive 互斥
INSERTUPDATEDELETESELECT ... FOR UPDATEshared_write(共享锁?)与 shared_read 和 shared_write 兼容,与 exclusive 互斥
ALTER TABLE ...exclusive(排他锁)与其他所有 MDL 互斥(不可共存’?)

查看元数据锁:select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;

注意:

MDL 是在事务提交后才会释放,这意味着事务执行期间,MDL 是一直持有的。

申请 MDL 锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现 MDL 写锁等待,会阻塞后续该表的所有 CRUD 操作。

所以为了能安全的对表结构进行变更,在对表结构变更前,先要看看数据库中的长事务,是否有事务已经对表加上了 MDL 读锁,如果可以考虑 kill 掉这个长事务,然后再做表结构的变更。

意向锁
  • 在使用 InnoDB 引擎的表里对某些记录加上「共享锁」之前,需要先在表级别加上一个「意向共享锁」;

  • 在使用 InnoDB 引擎的表里对某些纪录加上「独占锁」之前,需要先在表级别加上一个「意向独占锁」;

也就是,当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。

而普通的 select 是不会加行级锁的,普通的 select 语句是利用 MVCC 实现一致性读,是无锁的。【可以实现加行级锁】

【DML语句和for update会给表加上意向锁,同时会给索引添加行锁】

意向锁: 为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。

一个客户端对某一行加上了行锁,那么系统也会对其加上一个意向锁【因为是表锁,好检查】,当别的客户端来想要对其加上表锁时,便会检查意向锁是否兼容,若是不兼容,便会阻塞直到意向锁释放。

意向锁兼容性:

  1. 意向共享锁(IS):与表锁共享锁(read)兼容,与表锁排它锁(write)互斥。

  2. 意向排他锁(lX):与表锁共享锁(read)及排它锁(write)都互斥。意向锁之间不会互斥。

SIX

1.意向共享锁(IS):由语句select…lock in share mode添加

2.意向排他锁(IX):由insert,update,delete,select … for update添加

意向共享锁和意向独占锁是表级锁,不会和行级的共享锁和独占锁发生冲突,而且意向锁之间也不会发生冲突,

只会和共享表锁(lock tables ... read)和独占表锁(lock tables ... write)发生冲突。

表锁和行锁是满足读读共享、读写互斥、写写互斥的。

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

如果没有「意向锁」,那么加「独占表锁」时,就需要遍历表里所有记录,查看是否有记录存在独占锁,这样效率会很慢。

那么有了「意向锁」,由于在对记录加独占锁前,先会加上表级别的意向独占锁,那么在加「独占表锁」时,直接查该表是否有意向独占锁,如果有就意味着表里已经有记录被加了独占锁,这样就不用去遍历表里的记录。

所以,意向锁的目的是为了快速判断表里是否有记录被加锁

查看意向锁和行锁:select object_schema.object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.date.locks;

AUTO-INC 锁

AUTO-INC 锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放。

在插入数据时,会加一个表级别的 AUTO-INC 锁,然后为被 AUTO_INCREMENT 修饰的字段赋值递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。【其他的阻塞,从而保证插入数据时,被 AUTO_INCREMENT 修饰的字段的值是连续递增的。】

因此, 在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增。

一样也是在插入数据的时候,会为被 AUTO_INCREMENT 修饰的字段加上轻量级锁,然后给该字段赋值一个自增的值,就把这个轻量级锁释放了,而不需要等待整个插入语句执行完后才释放锁。

行级锁:

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。 InnoDB的数据是基于索引组织的【行数据是基于聚集索引组织的】,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:

  1. 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC(read commit )、RR(repeatable read)隔离级别下都支持。

  2. 间隙锁(GapLock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。比如说 两个临近叶子节点为 15 23,那么间隙就是指 [15 , 23),锁的是这个间隙。

  3. 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;

  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;

  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

1、行锁Record Lock

InnoDB实现了以下两种类型的行锁:

【只有共享锁之间是兼容的】

  1. 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。

  2. 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。

SQL行锁类型说明
insert排他锁自动加锁
update排他锁自动加锁
delete排他锁自动加锁
select不加任何锁通过mvcc实现的无锁
select…… lock in share mode共享锁需要手动在SELECT之后加LOCK IN SHARE MODE
select…… for update排他锁需要手动在SELECT之后加FOR UPDATE

默认情况下,InnoDB在REPEATABLE READ(RR,可重复读)事务隔离级别运行,InnoDB使用next-key 锁进行搜索和索引扫描,以防止幻读。

  1. 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。

  2. InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。【否则升级表锁】

间隙锁 Gap Lock / 临键锁Next-Key Lock

  1. 索引上的等值查询(唯一索引时),给不存在的记录加锁时,优化为间隙锁。

  2. 索引上的等值查询(普通索引),向右遍历时最后一个【因为可能不唯一】值不满足查询需求时,next-key lock 退化为间隙锁。【对该数据前间隙,和该数到后面第一个不满足的间隙加锁】

  3. 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。

如果查的id = 6没有,加个行锁(空的)还有间隙锁(左开右闭,1 ~ 7是中间的间隙)

mysql解决(快照读下)的幻读。简单的select直接通过readview读正确的数据。select for update不一样,因为要读到最新的数据

2、间隙锁 Gap Lock

【间隙锁是开区间的,是一个在索引记录之间的间隙上的锁。不让插入删除【解决脏读】,select for update】

间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的

3、临键锁Next-Key Lock

【临键锁是是一个左开右闭的区间,比如(- ∞, 1 ] |(1, 3 ] |(3, 4 ] | (4, + ∞)。】

next-key lock 即能保护该记录,又能阻止其他事务将新纪录插入到被保护记录前面的间隙中。

next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的。

默认情况下,InnoDB在REPEATABLE READ事务隔离级别运行,InnoDB使用next-key-lock (行锁 + 间隙锁)锁进行搜索和索引扫描,以防止幻读(不可控范围的话还是控制不了幻读问题)。

插入意向锁

插入意向锁名字虽然有意向锁,但是它并不是意向锁,它是一种特殊的间隙锁,属于行级别锁。

如果说间隙锁锁住的是一个区间,那么「插入意向锁」锁住的就是一个点。因而从这个角度来说,插入意向锁确实是一种特殊的间隙锁。

插入意向锁与间隙锁的另一个非常重要的差别是:尽管「插入意向锁」也属于间隙锁,但两个事务却不能在同一时间内,一个拥有间隙锁,另一个拥有该间隙区间内的插入意向锁(当然,插入意向锁如果不在间隙锁区间内则是可以的)。

附加:

这种查询会加锁的语句称为锁定读

 //先在表上加上意向共享锁(行锁),然后对读取的记录加共享锁
 select ... lock in share mode;
 ​
 //先表上加上意向独占锁,然后对读取的记录加独占锁
 select ... for update;
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值