悲观锁:获取数据时都会直接加锁,共享资源每次只给一个线程使用,其它线程阻塞等待。在数据库中提供了行锁、表锁等,操作数据时先加锁后使用。例如售票系统
select * from ticket where id=100 for update
乐观锁:不是数据库系统自带的,需要开发实现。乐观锁是只操作数据时并不进行任何特殊处理,也就是不加锁,在进行更新时才进行冲突判
在数据表中添加一个额外列:
version
数据版本号或者使用时间戳
timestamp
,每次修改数据则版号加1
update tb_users set age=80,version=2 where id=5 and version=1
第一次读取数据的版本
号为
1
,修改时条件为
version=1
的那条数据。如果中间有事务已经修改了数据,则版本号绝
对不是
1
,所以该语句执行返回结果为
0
如果执行结果为
0
,则重新读取数据,重新执行修改操作
锁模式
记录锁:在行相应的索引记录上的锁,锁定一个行记录
.
它会在
id=1
的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1
这一行。
gap
锁:间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一 条索引记录之后的范围
临键锁
next-key
锁是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。
MYSQL8 RR
级别下默认使用临键锁
.
临键锁的主要目的,也是为了避免幻读
(Phantom Read)
。如果 把事务的隔离级别降级为RC
,临键锁则也会失效
意向锁:是为了支持多种粒度锁同时存在;
锁分类
全局锁:就对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的
MDL
、
DDL
语句、更
新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有的表进行锁
定,从而获取一致性视图,保证数据的完整性
flush tables with read lock;
unlock table
行级锁
S 共享锁 :允许获取到此锁的事务读取行
X 排他锁 :允许获取到此锁的事务
update
,
delete
行
表级锁
-
意向锁
表级锁会对当前操作的整张表加锁,最常使用的 MyISAM
与
InnoDB
都支持表级锁定
lock tables xxx read/write;
IS 意向共享锁:事务打算对表中的行设置共享
S
锁
IX 意向排他锁:事务打算对表中的行设置排他
X
锁
意向锁是 InnoDB
自动加的, 不需用户干预
意向锁定协议
事务在获得表中某行上的共享锁之前,必须先获得表上的IS
锁或更高级别的锁
在事务可以获得表中某一行上的排他锁之前,它必须首先获得表上的IX
锁
验证,比如表
aa,
需要开启
set
AUTOCOMMIT
=
FALSE
;
X:
lock tables
aa
write
;
S:
lock tables
aa
read
;
IX:
select
*
from
aa
where
1
=
2
for update
;
IS
:
select
*
from
aa
where
1
=
2
lock in share mode
;
按加锁方式分类
按加锁方式划分,可分为自动锁、显示锁。
隐式加锁:
InnoDB自动加意向锁
对于UPDATE
、
DELETE
和
INSERT
语句,
InnoDB
会自动给涉及数据集加排他锁
对于普通SELECT
语句,
InnoDB
不会加任何锁;
随时都可以执行锁定,InnoDB
会根据隔离级别在需要的时候自动加锁;锁只有在执行commit或者rollback
的时候才会释放,并且所有的锁都是在同一时刻被释放。
显式加锁:
共享锁S
:
SELECT * FROM table_name WHERE … LOCK IN SHARE MODE
排他锁X
:
SELECT * FROM table_name WHERE … FOR UPDATE
按照算法分类
按照算法分类,可分为间隙锁、临键锁、记录锁。
间隙锁:间隙锁基于非唯一索引,它锁定一段范围内的索引记录。使用间隙锁锁住的是一个区间,
而不仅仅是这个区间中的每一条数据。
select * from tb_users where id between 1 and 10
for update
;
即所有在(
1
,
10
)区间内的记录行都会被锁住,所有
id
为
2
、
3
、
4
、
5
、
6
、
7
、
8
、
9
的数据行的插入会被阻塞,但是
1
和
10
两条记录行并不会被锁住
临键锁是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间,是一个左开
右闭区间。临键锁的主要目的,也是为了避免幻读
Phantom Read
。如果把事务的隔离级别降级为
RC
,临键锁则也会失效。每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该
数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,
InnoDB
中行级锁是基
于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
记录锁是封锁记录,记录锁也叫行锁,如
select *from tb_users where id=1 for update
;
它
会在
id=1
的记录上加上记录锁,以阻止其他事务插入,更新,删除
id=1
这一行。
相关锁总结
记录锁、间隙锁、临键锁,都属于排它锁
记录锁就是锁住一行记录
间隙锁只有在事务隔离级别
RR
可重复读 中才会产生
唯一索引只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁
普通索引不管是锁住单条,还是多条记录,都会产生间隙锁
间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据,这是为了防止出现幻读现象
普通索引的间隙,优先以普通索引排序,然后再根据主键索引排序
事务级别是
RC
读已提交级别的话,间隙锁将会失效
表级锁:
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高
,
并发度最低。
行级锁:
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低
,
并发度也最高。
页面锁:
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度