加锁的目的是为了解决隔离性
问题,让事物之间互不影响,每个事物在操作数据前都加一把锁,若后来的事物发现持有锁只能等待。
锁定分类有一下几种:
基于锁的属性分类: 共享锁、排他锁。
基于锁的粒度分类: 表锁、行锁(记录锁、间隙锁、临键锁)。
基于锁的状态分类: 意向共享锁、意向排它锁。
共享锁又称为S锁
,当一个事物A对数据加了共享锁之后,后续的事物只能读取数据,而不能做任何修改操作,也就是不能加写锁了,只有事物A的释放后,其他事物才能添加写锁。
实现方式: select …lock in share mode
排他锁又称为写锁,简称为X锁
,当一个事物A对数据加了排他锁之后,后续的事物只能等待,无法读取也无法修改,等待事物A释放之后才能做后续的操作。
实现方式: select …for update
意向锁是 表锁 ,为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。当一个事物试图对整个表进行加锁之前首先需要获取对应类的意向锁。
意向共享锁: 当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
意向排他锁: 当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。
意向锁作用:
1、事物A执行了:update xxxx set name = ‘张三’ where id = 6 ,此时事物A就会对id为6的这条数据进行加排他锁。
2、事物B此时执行了:update xxxx set name = ‘李四’ ,此时事物B对整个表进行操作,所以想要获得表级别排他锁,首先检查这个表有没有被其他的事物锁住,若没有再去检查每一行的数据是否被锁住,然后自己在加锁成功或失败。
所以,事物B需要验证遍历表的所有数据才能判断出是否加锁,然后才能继续操作,浪费了大量的性能,所以出现了意向锁 。
如果事物A加锁成功之后就设置一个标志,后续来的事物观察这个标志判断是否可以对整个表进行加锁,以此就不需要遍历每条数据了,提高性能。
表锁顾名思义指锁住的是整个表。特点是粒度较大,锁住整个表,易冲突但好维护。
行锁是对行级别所得一个统称,比如 记录锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next-Key Lock ),指锁住一行或者多行数据,若多个事务访问同一张表,只有被锁住的数据不能访问,其他数据可以访问。
注意: 行锁都是需要命中索引才会生效的,若表没有索引则都是表锁。
记录锁是属于行锁的一种,记录锁的范围是表中的一条数据。
select * from test where id = 10 for update;
这条SQL就会命中id为10的这条数据。
触发条件: 精准命中条件,并且命中索引。
间隙锁是锁住表中的某个索引区间,是一个范围锁。使用间隙锁锁住的是一个区间,不包括具体数据,锁住的是空闲的空间。
数据如下所示:
这儿会形成(- ∞ ,1),(1,10),(10,20),(20,30),(30,+ ∞ )这几个区间,间隙锁是开区间
。
SELECT * FROM aa_test WHERE order_no BETWEEN 10 AND 20 FOR UPDATE;
间隙锁会找寻范围值最小值的前一个区间段以及返回值最大值的后区间段作为锁定区间,比如这儿间隙锁锁定的就是(1,10),(10,20),(20,30)
这个区间段。
若此时另一个事物执行如下sql:发现被阻塞了。
INSERT INTO `aa_test`( `order_no`) VALUES (4);
是行锁的一种,innodb的默认行锁算法,总结来说就是记录锁
以及间隙锁
的结合,会把真实的记录锁住,也会锁住区间内的空闲空间。
还用上面的数据进行讲解,会形成(- ∞ ,1],(1,10],(10,20],(20,30],(30,+∞),这些区间包含具体的数据。
SELECT * FROM aa_test WHERE order_no BETWEEN 1 AND 13 FOR UPDATE;
这条SQL会锁住(- ∞ ,1],(1,10],(10,20] 区间的数据。
1、查看是否锁表
show status like ‘%lock%’
2、查询进程
show processlist;
3、查询行锁、表锁状态
## 查看行锁的状态
show status like 'innodb_row_lock_%';
##查看表锁的状态
show status like 'table%';
4、INFORMATION_SCHEMA库
## 内核中的当前活跃(ACTIVE)事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
## 查看正在锁的事物,被阻塞的
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
## 查看等待锁的事物,被阻塞的
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;