InnoDB锁总结

1. InnoDB锁

InnoDB采用行锁,根据事务级别分别采用不同的锁。一般情况下,如果二级索引加锁时,会同时去找对应的聚集索引加锁。如果无索引时,读时逐行扫描锁全表。

(1) S锁,共享锁;X锁,排他锁。S锁和S锁可以共存,S锁和X锁互斥。

(2) 意向锁【表级锁,多粒度锁支持表锁和行锁共存】

InnoDB允许表锁和行锁共存,意向锁是数据库表级别的锁,指出事务稍后对表中的行需要哪种类型的锁(X锁或者S锁)。意向锁只会阻塞锁全表,它只是表明某个事务已经获取或想要获取某行的锁。

# select..lock in share mode IS锁,在事务获取行的S锁前,该事务必须获取表的IS锁或者更强的锁;

# select..for update IX锁,在事务获取行的X锁之前,该事务必须获取表的IX锁;

 XIXSIS
X互斥互斥互斥互斥
IX互斥共享互斥共享
S互斥互斥共享共享
IS互斥共享共享共享

# 总结

X锁和任何锁都互斥,S锁和S锁,IS共存;IX锁和IX,IS锁共存;IS锁和除X锁外共存

(3) 行记录锁【锁索引】

记录锁是指记录索引上的锁,记录锁总是第一时间去锁索引,若表上没有定义索引,则锁隐藏的聚集索引。

(4) gap间隙锁【区间】

间隙锁是指索引上的间隙锁,或对第一个索引记录之前或最后一个索引记录之后的间隙的锁。只阻塞其他事务在间隙区间内插入新记录,对于其他事务获取相同的间隙锁不阻塞。column=x时,该列有唯一索引不会触发gap锁,只会触发行锁,其他情况下会触发gap锁,锁(0,x];触发当事务级别为read committed或者innodb_locks_unsafe_for_binlog开启时,间隙锁被禁用。

# select c from t where c between 10 and 20; 区间范围为10-20,则区间范围内不允许插入任何行。

(5) Next-key

记录锁和gap锁的结合,index行锁+index之前的gap锁,不允许index前insert任何数据。

默认REPEATABLE READ隔离级别下,InnoDB采用next-key,为扫描搜索表索引时遇上的行添加上S锁或者X锁。当一个事务拥有一行的记录锁时,阻塞其他事务在该行的索引顺序之前的间隙中插入新行,避免幻读。

(6) Insert意向锁

由insert操作发起,是一种gap锁。若多个事务在相同的间隙范围内插入行的位置不同,该锁不会阻塞对方。

(7) Auto-inc锁

表级锁,由insert操作发起,若有自增的列。阻塞其他事务。

(7) Predicate锁

表空间索引锁,包含最小边界矩阵,阻塞其他事务insert和修改。

2. 事务级别

read uncommited会读到其他事务未提交的数据,脏读,无锁
read commited

# 同一个事务内的的select都获取当前最新版本数据

# gap锁禁用

# 对于加锁读,update,delete语句,只锁索引记录,允许insert到行锁的临近位置,gap锁只在外键时采用;会读到其他事务在当前事务提交前提交的数据;

# update或delete加锁时,先锁住扫描时遇到的行,一行行处理时,经过评估释放不满足where条件的行锁,一直持有满足条件的行锁

# update语句,如果一行已经锁住了,InnoDB采用半一致读将最新提交的版本返回给mysql以判断这行是否满足where条件,若满足mysql锁住该行不满足释放锁。

#无索引时,锁全表

repeatable read

# InnoDB默认隔离级别,事务内一致读,每次都读第一次读时的产生的快照版本

# 对于加锁读,uodate,delete等,若表中有唯一索引且搜索采用唯一索引时,只锁满足条件的行,其他情况,一直锁住扫描的索引区间,采用gap锁或者next-key阻塞其他事务insert到相同区间

# 先锁住扫描时遇到的行,直到执行结束释放锁

# 无索引时,锁全表

# 一致读时,InnoDB为事务设置了一个时间点,这个时间点之后的修改或插入数据,事务内是不能读到的。但是,该snapshot对dml操作却没有作用,该事务可能会修改其他事务已经提交的修改。避免这种操作的方式就是select后提交事务,重新起另一个事务select后操作。

# 当事务A修改

serializable

gap锁或者next-key

autocommit禁用时,隐式地将select语句转换为加IS锁模式

 

一致读在ddl时不起作用:

(1) drop table;Mysql不能使用已经删除的表

(2) alter table;该语句创建了一个临时表拷贝了原表数据,然后删除原表

3. 加锁sql

(1) select..lock in share mode

采用next-key,为读到的行加S锁,其他事务只能读不能修改这些行,如果该事务之前有其他事务正在修改这些行,那么该查询等待其他事务结束,并读到最新数据。

(2) select..for update

采用next-key,为扫描遇到的行加X锁,并且锁住任何索引入口。

(3) update..where

采用next-key加X锁,更新聚集索引时为二级索引加S锁,插入新二级索引时校验重复key。

(4) delete..where

采用next-key加X锁

(5) insert

采用索引记录锁加X锁,执行insert先设置一个意向gap锁,同区间内不同位置不会阻塞。

若重复key错误发生时重复记录设置一个S锁,可能引起死锁:

事务A inset 1,获取X锁;事务B insert 1, 等待;事务C insert 1, 等待;若事务A回滚释放X锁,则BC死锁。

事务A delete 1,获取X锁;事务B insert 1, 等待;事务C insert 1, 等待;若事务A提交释放X锁,则BC死锁。

(6) insert...on duplicate key update

若重复key错误发生时,重复主键设置一个X锁(索引记录锁),重复唯一键设置X锁(next-key)不会发生死锁。

(7) replace

采用next-key加锁,在没有冲突时和insert一致,否则加X锁

(8) insert into T select..from S where

T表加X锁(索引记录锁),若事务级别为read committed或者innodb_lock_unsafe_for_binlog启用,S表无锁,其他情况下S表加S锁(next-key)

(9) create table..select...

与8一致

4. 避免死锁

(1) 更新数据尽量采用小事务,避免占用锁过长时间;

(2) 更新时顺序一致;

(3) 设置innodb_lock_wait_timeout

(4) insert, update, delete操作尽量放一起;

(5) 合理的添加索引;

5.事务举例 [read-commit事务级别]

本地事务中注意控制幂等性,单纯的记录锁不能保证数据操作安全

时间T1  order(id, price)T2  order(id,version, price)
1begin;begin;
2

update order set price=1 where id=1 and price=0

获取id=1行X锁,执行成功影响1行

 
3 

update order set price=2 where id=1 and price=0

等待id=1行X锁,该行后若有sql则阻塞等待

4

commit; (释放锁,持久化)

update order set price=3 where id=1 
5 

获取行锁,第一条执行成功影响0行,因为price已经不等于0了

第二条执行成功覆盖T1值

commit; (释放锁,持久化)

6 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值