【官方文档整理】Mysql中的锁
目录
以下内容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)
- 锁某个间隙(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锁
是记录锁和间隙锁(锁某个索引记录之前的间隙,before the index record)的组合形式,在RR级别下被运用。当在查询和浏览索引时,会为每一个遇到的索引添加记录锁,假设执行下面的sql,显然InnoDB要扫描id为10,11,13,20的row,因此会为这些row添加记录锁,除此之外,也会为加锁记录前的间隙加锁。整个SQL执行后,最终的锁定范围如下,锁定范围内不能进行增删改操作,避免出现幻读。
- 会锁定(上一个存在索引,当前添加记录锁的索引]。
- 对于遍历索引中的最后一个记录,会锁定它后面的间隙。
# tab表中rows的id包括:4,10,11,13,20
select * from tab where id > 5 for update;
# 整个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操作的事务并发量】之间的平衡。
算法以后有时间再看。。。
推荐阅读
-
MySql官方手册学习笔记3―MySql中的存储过程简介_MySQL
-
PHP中GD库的官方站文档中有显示ImageGifAnimBegin这个函数,为什么小弟我用在PHP中会提示undefined function
-
关于MySQL InnoDB存储引擎中的锁
-
PHP中GD库的官方站文档中有显示ImageGifAnimBegin这个函数,为什么小弟我用在PHP中会提示undefined function
-
MySQL InnoDB中的锁机制深入讲解
-
高性能MySQL--innodb中事务的隔离级别与锁的关系
-
重新学习MySQL数据库6:浅谈MySQL的中事务与锁
-
MySQL 笔记整理(7) --行锁功能:怎么减少行锁对性能的影响?
-
mysql中的锁机制深入讲解
-
Mysql中key和index的区别点整理