【官方文档整理】Mysql中的锁

目录

共享锁(S)和排它锁(X)

意向锁(Intension Locks)

记录锁(Record Locks)

间隙锁(Gap Locks)

next-key锁

插入意向锁(Insert Intention Locks)


以下内容90%来自mysql 5.7官方文档,有兴趣的可以直接看原文。

共享锁(S)和排它锁(X)

都属于行锁,当一行数据被添加共享锁时,其它事务只能读取不能修改;当一行数据被添加排它锁时,其它事务的读取和修改操作都被禁止。

意向锁(Intension Locks)

分为意向共享锁(IS)和意向排它锁(IX)。意向锁协议:1、一个事务在获取S锁之前必须先或许IS锁;2、一个事务在获取X锁之前 必须先获取IX锁。一个事务获得IS锁表示这个事务准备为一行记录添加S锁,一个事务获得IX锁表示这个事务准备为一行记录添加X锁,意向锁存在的目的就是为了说明有事务正在或将要锁定一行记录,那这到底有什么意义呢?

在MySql中,可以为一张表添加S/X锁,也可以为一张表中的某几行添加S/锁,如下。显然,事务A获取了tab表的X锁,事务B就不能获取其它任意锁;同理,事务A获取了id=1的X锁,事务B就不能获取tab表的X/S锁,其它情况就不一一介绍了。在没有意向锁的情况下,事务A想要获取tab表的X锁,则必须遍历tab中所有的记录,看看有没有加X/S锁的情况,有就不能获取表的X锁,这样就很麻烦。但是,有了意向锁就不一样了,上面说了,意向锁存在的目的就是为了说明有事务正在或将要锁定一行记录,我不管这里的事务有多少个,不管要锁定的行记录有多少条,意向锁只说明“有事务锁定了某行”即可,可以把IS锁和IX锁看成两个字段boolean flagIS和boolean flagIX。这时如果事务A要获取tab表的X锁,只要判断(flagIS || flagIX)是否为true,是就不能获取表锁,否就直接获取;如果事务A想要获取tab表的S锁,就判断(flagIX)是否为true即可,这样就省去了遍历整张表的麻烦。

lock tables tab write                          # 为表tab添加X锁
lock tables tab read                           # 为表tab添加S锁
select * from tab where id = 1                 # 为表tab中id=1的记录添加S锁
update tab set name = 'zkw' where id = 1       # 为表tab中id=1的记录添加X锁

意向锁仅仅会阻塞获取整张表的锁的请求,不会阻塞任何其他请求,IS和IX锁之间是兼容的,事务A获取了IX锁,事务B依然可以获取IS锁和IX锁。

记录锁(Record Locks)

记录锁锁一个具体的索引记录(index record),就是对一行数据上的索引进行加锁。比如下面SQL执行后,Mysql会阻止其它事务插入、修改、删除t.c1=10的记录。当某个表没有定义索引,比如t.c1不是索引,InnoDB会创建一个隐藏的聚簇索引,然后用这个索引来作为记录锁锁定的对象。

SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;

间隙锁(Gap Locks)

在RR隔离级别下,select...for update和select...lock in share mode等这些查询会默认加间隙锁。

在read-commited隔离级别下被禁用。

  • 锁某个间隙(gap),这个间隙可能是两个索引记录之间的范围(如a~b),也可能是a之前,也可能是b之后。间隙可能跨越(span)单个索引值、多个索引值,甚至可以是空(empty)。
  • 间隙锁的目的是防止因其它事务不合时宜的插入导致的幻读。
  • 当使用唯一索引(唯一索引不允许具有索引值相同的行,如主键)进行等值查询时不会出现间隙锁,如其它情况则会出现。如下面sql,id为主键,则执行select语句后仅仅使用记录锁对id=100的行的索引加锁,不会锁住前面的间隙(the preceding gap),如id<100的间隙。如果id不是索引或者是一个非唯一索引,那么这条语句就会锁住前面的间隙。
SELECT * FROM child WHERE id = 100;
  • 间隙锁出现在RR隔离级别中,RU和RC中没有间隙锁。

next-key锁

在RR隔离级别下,select...for update和select...lock in share mode等这些查询会默认加next-key锁。

是记录锁和间隙锁(锁某个索引记录之前的间隙,before the index record)的组合形式,在RR级别下被运用。当在查询和浏览索引时,会为每一个遇到的索引添加记录锁,假设执行下面的sql,显然InnoDB要扫描id为10,11,13,20的row,因此会为这些row添加记录锁,除此之外,也会为加锁记录前的间隙加锁。整个SQL执行后,最终的锁定范围如下,锁定范围内不能进行增删改操作,避免出现幻读。

  • 会锁定(上一个存在索引,当前添加记录锁的索引]。
  • 对于遍历索引中的最后一个记录,会锁定它后面的间隙。
# tab表中rows的age包括:4,10,11,13,20
select * from tab where age = 10 for update;    # 加锁范围:(4,10]
select * from tab where age > 13 for update;    # 枷锁范围:(13,20]U(20,positive infinity)


# 整个next-key锁可能加锁的范围
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

插入意向锁(Insert Intention Locks)

插入意向锁就是一种类型间隙锁,在执行insert操作时加锁到insert索引的上下索引区间上,如数据库中有4,7两条记录,你插入5或6,就会给(4,7)这个区间加插入意向锁。

它的作用和上面的意向锁一样,也仅仅是为了说明在某个区间内有事务准备或正在执行insert操作,这样一来,那些想要给这个区间加间隙锁或next-key锁(就是锁住这个区间,不让其他事务读或写这个索引区间的数据)的事务直接判断这个区间有没有插入意向锁,有就等待,没有就锁住,避免了需要遍历这个区间的每条数据来查看是否有加锁的情况。

多个事务可以同时获取同一个区间的插入意向锁,只要他们插入记录的索引值不相同,那就不会发生阻塞情况,如上面例子,事务A和事务B分别插入5、6,都可以正常插入。

下面因子官方案例,经过自己测试,在创建child表和插入90、102两条数据后,不管是事务A或事务B谁先执行,后执行的事务总是会阻塞。道理很简单,我查这个区间时,你不能往这个区间里面插入数据;我往这个区间插入数据时,你不能查这个区间。

自增锁(AUTO-INC Locks)

自增锁是一种特殊的表锁,只针对有自增列的表。举个最简单的例子,当事务A往有自增列的表insert一条数据时,事务B必须阻塞自己的insert操作,以致于事务A插入数据的列是正确的。这样可能还不太明白,这就跟多线程情况下执行i++是一个道理,如果不对i++这个动作加锁,那么很可能会出现线程i++后i=1,线程2i++后i也等于1的情况。

InnoDB中通过配置“innodb_autoinc_lock_mode”选项来指定使用自增锁时的算法,通过这些算法能调整【自增值的可预测性】和【执行insert操作的事务并发量】之间的平衡。

算法以后有时间再看。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值