锁作用
-
保证共享资源一致性
-
实现事务隔离性
锁粒度(等级)
- 表级锁:开销小,加锁快; 不会死锁;锁定粒度大,发生锁冲突的概率最高 ,并发度最低
- 行级锁:开销大,加锁慢;
会出现死锁(锁是逐步获得的)
;锁定粒度最小,发生锁冲突的概率最低,并发度也最高 - 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
存储引擎各自实现锁机制,server层不感知存储引擎的锁实现
InnoDB锁模式
行锁
-
共享锁(S)
-
排他锁(X)
表锁
-
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁
-
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁
允许行锁和表锁共存,实现多粒度锁机制
隐式& 显式 锁定
隐式锁定
事务执行过程中,使用两阶段锁协议:
-
随时都可以执行锁定,InnoDB会根据隔离级别在需要的时候自动加锁
-
锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在 ``同一时刻` 被释放
显式锁定 :
select ... lock in share mode
select ... for update
乐观&悲观锁
乐观锁
select id,value,version from TABLE where id = #{id}
更新时,为防止发生冲突,需要这样操作
update TABLE
set value=2,version=version+1
where id=#{id} and version=#{version}
失败后重试或回滚
悲观锁
直接调用数据库的相关语句
悲观锁涉及的另外两个锁概念:共享锁与排它锁
共享锁(S):
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
确保自己查到的数据没有被其他的事务正在修改,也就是说确保查到的数据是最新的数据,并且不允许其他人来修改数据(获取数据上的排他锁)
自己不一定能够修改数据,因为有可能其他的事务也对这些数据 使用了 in share mode 的方式上了 S 锁
当前事务对读锁进行修改操作,很可能会造成死锁
create table innodb_lock(
id bigint primary key auto_increment,
v int
);
insert into innodb_lock (id,v) values(1,'dd');
Transaction A | Transaction B |
---|---|
set autocommit=0;select * from innodb_lock where id=1 lock in share mode; | set autocommit=0;select * from innodb_lock where id=1 lock in share mode; |
update innodb_lock set v=2 where id=1; 等待 |
|
等待 | update innodb_lock set v=2 where id=1; Deadlock found when trying to get lock; try restarting transaction |
Affected rows: 1 |
排他锁(X):
SELECT * FROM table_name WHERE ... FOR UPDATE
自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改