MySQL~锁的大集合(S锁、X锁、意向锁、自增锁、元数据锁、记录锁、间隙锁、临键锁、全局锁、死锁)


什么是锁机制

锁是计算机协调多个进程或线程并发访问某一资源的控制.

  • 锁不仅仅限于数据库领域,在计算机中,当多个进程或线程并发的访问某个数据的时候,对于一些重要敏感的数据,为了保证数据的完整性一致性,我们需要保证最多只有一个线程在访问,所以诞生了锁机制
  • 锁机制的作用是对并发操作进行控制
  • 在MySQL中,锁保证了事务的隔离性

并发的访问相同数据的情况

读是查询操作,写是增删改操作.

读读

两个事务并发读取相同数据的情况.

  • 读读操作都是查询操作,本身对数据没有什么影响,所以没有什么问题.
写写

两个并发事务相继对同一个数据进行修改的情况.

  • 写写情况下,会发生数据脏写问题,脏写问题非常严重,任何的事物隔离级别都不允许发生这个问题
  • 为了避免脏写,数据库强制未提交事务必须排队执行,通过锁实现强制.
  • 假设事务T1想要修改一条记录,如果内存中没有与之相关的锁结构,会生成一个锁结构与它进行关联。简化图:.
    在这里插入图片描述

锁结构中,trx标识该锁结构属于哪个事务,is_waiting标识该事务是否在等待

  • T1之前没有任何事务对该记录进行修改,所以is_waiting是false,表示无须等待,可以直接修改,即加锁成功.在事务T1未提交之前,如果事务T2也想对该记录修改,就需要等待,is_waiting是true,需要等待,即加锁失败.

在这里插入图片描述

  • 在事务T1提交后,锁会释放,交给T2,T2的is_waiting变为false,然后T2就会开始执行.
读写/写读

两个并发事务,对于同一条数据,一个读取,一个修改。

  • 读写/写读情况下会发生脏读、不可重复读、幻读问题
  • 解决这些问题有两个办法:一是对读操作使用MVCC,写操作进行加锁.方法二是读写操作都进行加锁.

锁的分类

  • 对数据的操作类型划分:共享锁(读锁)、排他锁(写锁)
  • 从锁粒角度划分:表锁、行锁、页锁
  • 从锁的态度进行划分:悲观锁、乐观锁
  • 从加锁方式划分:隐式锁、显式锁
  • 从其他角度划分:全局锁、死锁
共享锁(S锁)
  • 共享锁(Shared Lock) 也叫读锁,对于同一份数据,多个事务进行读取时可以同时进行互不影响
排他锁( X锁)
  • 排他锁(Exclusive Lock)也叫写锁、X锁,对于同一份数据,当一个数据进行写操作时,禁止其他事务的读、写操作.

对于Innodb引擎,读锁和写锁可以加在表上,也可以加在行上.

两个锁之间的兼容性
在这里插入图片描述
锁定读操作

读取时加S锁:
SELECT ... Lock in share mode;
或者
select....for share;
为这条记录添加了S锁后,就不允许其他事务获得该记录的X锁
读取时加X锁:
SELECT.... for update:

为这条记录加了X锁后,就不允许其他事务获得该记录的S、X锁.

锁定写操作

写操作只有三种:deleteupdateinsert

对于delete操作:获取记录位置,然后获取它的X锁,再进行deletemark操作----可以理解为获取X的锁定读

对于update操作:
获取记录位置,相当于获取X锁,锁定读。然后主键更改,就删除,并重新添加一个,如果没更改,就直接在原记录中更新.

对于insert操作:
并不加锁.有隐式锁来保护不被其他事务打扰.

表锁

表锁会锁住整张表,是MySQL中最基本的锁策略,并且不依赖于任何存储引擎.

表级别的S、X锁.
  • 对表执行普通的增加删除修改更新操作时,不会添加表级的S、X锁.
  • 当事务A对表执行增加删除修改更新操作,事务B并发执行影响表结构的操作(如alter tabledrop table)时,才会有表级锁的出现,对事务进行阻塞
  • 同理,事务A对表结构进行修改时,如果事务B并发执行普通的增删查改,也会发生阻塞.

为表添加S、X锁

LOCK TABLES T read;

LOCK TABLES t WRITE;

虽然可以手动添加表锁,但一般很少使用,因为有更强大的行锁

限制关系
在这里插入图片描述

对限制关系的演示

初始化数据
mysql> create table mylock(
    -> id int not null primary key auto_increment,
    -> name varchar(20)
    -> )engine myisam;
Query OK, 0 rows affected (0.06 sec)

mysql> insert into mylock(name) values('a');
Query OK, 1 row affected (0.04 sec)

加锁
mysql> lock tables mylock read;
Query OK, 0 rows affected (0.00 sec)

加锁后自己可读
mysql> select*from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)

自己不可写
mysql> update mylock set name='a1' where id=1;
## ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated;

#自己不可操作其他表
mysql> select *from account;
## ERROR 1100 (HY000): Table 'account' was not locked with LOCK TABLES

他人可读
mysql> select* from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)
他人不可写 会阻塞
mysql> update mylock set name='a2' where id=1;

释放锁
mysql> unlock tables ;
Query OK, 0 rows affected (0.00 sec)

改为写锁
mysql> lock tables mylock write;
Query OK, 0 rows affected (0.00 sec)

自己可以读取
mysql> select*from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)
自己可以写入
mysql> update mylock set name='a3' where id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
自己不能操作其他表
mysql> select*from account;
ERROR 1100 (HY000): Table 'account' was not locked with LOCK TABLES

他人读取会阻塞
mysql> select*from mylock;
## 阻塞
他人写入会阻塞
mysql> update mylock set name='a4' where id=1;
## 阻塞
意向锁.

事务A对某一行数据加了一个锁,事务B如果想对整张表加一个锁,就需要检查该表中是否已经存在锁,在数据量非常大的情况下,一页一页的检索是非常复杂的一件事情.此时就可以用到意向锁.

  • 意向锁是表级别的锁,对整张表添加
  • 意向锁是存储引擎自动添加,不需要手动添加
  • 意向锁不会与行锁发生冲突
  • 意向锁分两种:意向共享锁、意向排他锁
  • 如果我们给某一行数据加上了排他锁,数据库会自动给更大一级的空间(比如数据页、数据表)加上意向锁,告诉他人这个数据页或数据表已经有人上过排他锁了

意向共享锁(IS锁)
存在事务想对表中的某个数据添加共享锁

--事务想要获取某项行的S锁,必须先获得表的IS锁
select...from table...lock in share mode;

意向排他锁(IX锁)
存在事务想对表中的某个数据添加排他锁

---事务想要获得某项行的X锁,必须先获得表的IX锁
select ... from table...for update;
  • 意向锁之间相互兼容,互不影响
  • 意向锁和表级别的X,S锁不兼容,除了IS和S
自增锁
  • 特殊的表级锁,事务向AUTO_INCREMENT字段添加新数据时就会持有自增锁
  • 如果事务A正在向自增列添加新数据,此处事务B尝试INSERT,就会被阻塞.
  • 不同锁模式下运行机制不同,行为不同,锁模式通过参数innodb_autoinc_lock_mode进行设置
  • 自增列必须是索引才能用自增锁.
元数据锁
  • 元数据锁(meta data lock,MDL锁)是表锁.
  • 当事务对一个表进行增删改查时,会自动添加MDL读锁,当事务对表结构进行修改的时候,会自动添加MDL写锁
  • 自动添加MDL锁的作用就是防止表在增删改时,表结构被改动。
  • 读读不互斥、读写、写写互斥
  • 自动添加,无须手动

行锁

  • 对某一条记录进行加锁
  • 优势是锁定单位小,所以冲突概率低,并发性非常高,劣势是会用太多的锁资源,加锁慢,容易出现死锁问题
  • Innodb和MyISAM引擎最大的不同:支持事务;支持行锁
记录锁
  • 非常普通的锁,就是对某一条记录加锁
  • 分为S、X型。
  • 当事务获的某条记录的S型记录锁后,其他事务只能获取S型
  • 当事务获得某条记录的X型锁后,其他事务只能等待
    在这里插入图片描述
间隙锁
  • 我们可以通过加锁的方式解决幻读问题,但是问题是幻读产生的那些幻影记录一开始并不存在,无法加锁,为了解决这个问题,就产生了间隙锁gap锁
  • 间隙锁只有一个作用:防止插入幻影记录
  • 比如我们对id=8的记录加上间隙锁,那么上一条记录和id=8的记录之间就不允许插入新的记录,如果其他事务想要插入新的记录,会被阻塞.
  • 对某记录之后的区间加上gap锁,需要用两条伪记录:Infimum记录,表示该页中最小记录;Supremum记录,表示该页中最大的记录;
  • 直接对Supremum记录加gap锁即可
临键锁
  • 临键锁=记录锁+间隙锁
  • 作用:锁住某条记录,并阻止其他事务在该事务前面的间隙插入事务
  • 分为临键S和临键X锁,互斥情况和记录锁相同
begin;
select*from student where id<=8 and id>3 for update;
插入意向锁
  • 插入意向锁是在插入一条记录时,由INSERT操作产生的一种间隙锁.
  • 插入意向锁本质是间隙锁
  • 在插入一条记录时,需要看插入位置是否被别的事务加了间隙锁,如果有,需要等待,知道间隙锁的事物提交.等待时,会生成一个锁结构,表名某事务有在某位置插入数据的意图,这个锁结构就是意向锁
  • 插入意向锁互不排斥

当事务T1结束后,事务T2和T3都会获取到插入意向锁:
在这里插入图片描述

页锁

  • 粒度大小介于表锁和行锁之间
  • 开销介于表锁和行锁之间
  • 锁的空间大小有限,超过大小时,会自动进行锁升级.

粒度越大,开销越小。粒度大小:表>页>行。开销:表<页<行

其他锁

全局锁
  • 对整个数据库实例加锁,整个库处于只读的状态
  • 增删查改、修改表结构的等都会阻塞
  • 使用场景:全库备份(对数据库进行备份时,不允许操作)
  • 命令:Flush tables with read lock
死锁
  • 两个或多个事务在同一资源上进行占用,都有对方的需要的锁,但都不释放,陷入死循环
  • 例如下图:事务1对id=1的记录设置了X锁,事务2又对id=2的记录设置了X锁。事务1在尝试更新记录2时陷入阻塞,事务2再尝试更新记录1时陷入阻塞。然后两个事务互相僵持,陷入死锁状态
    在这里插入图片描述

死锁条件

  • 必须有两个事务
  • 每个事务都持有锁,还申请新的锁
  • 新锁恰好为对方所有

如何处理死锁

  • 方法1:等待,直到超时

  • 两个事务互相等待,当一个事务等待时间超过设置的阈值时,就将其回滚。另一个事务就可以继续进行。

  • Innodb中,使用innodb_lock_wait_timeout设置时间

  • 方法2:使用死锁检测进行死锁处理

  • innodb中有wait-for graph算法主动检测死锁,每次加锁需要等待时就会触发

  • 死锁检测原理:
    数据库会保存锁的信息链表事务等待链表

在这里插入图片描述
根据上面两个信息,可以画出等待有向图:
在这里插入图片描述
在等待图中,如果有环的存在,就是死锁。

  • 如果出现死锁,存储引擎就会回滚操作量最小的事务.
  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值