mysql锁
表锁
顾名思义锁住整张表。如果没有加索引检索就会触发表锁。性能最低
行锁
1.锁住一行数据,插入和更新都会使用行数。mysql使用索引命中时必用。
2.RR可重复读模式下使用索引where检索则会开启间隙锁或临键锁。防止数据重复插入。解决当前读的幻读(快照读被mvcc解决)
间隙锁和临键锁(临键锁就是间隙锁+行锁)
备注:对于主键索引和唯一索引检索时对于未命中的索引列会直接开启间隙锁
间隙锁锁定规则是从检索数据左边第一个索引记录数,到行锁的数据。
再从行锁的数据到右边第一个索引记录数的两个区间。
以下图的索引数据举例.图片摘取自网络
首先要理解索引的排序规则
1聚簇索引会按照b+数有序排序。b+树分三层。顶层为根节点。二层存储key也就是主键聚簇索引的id。最后一层存储数据。b+树按照id有序排序和检索。
2非聚簇索引同理。顶层存储根节点,二层存储的是普通索引的key。那么底层是key对应的主键id。根据主键再去查询聚簇索引的b+树
重点规则是非聚簇索引树会先按照非聚簇索引的key大小进行排序,如果重复再按照主键id进行排序。
看上图,实际就是一个非聚簇索引对应的一个聚簇索引id
如果select * from t where num=4;
此时4的左右间隙都已被锁定。4左边(1,2)右边(6,5)
那么此时(2,3)(2,4)(2,5)(4,4)(4,5)(5,5)都将不能插入,或者将另一个不在该位置的数据修改到该位置也是违背规则的。
比如update t set num=4 where id=8此操作会将id=8的数据移动到(3,4)的下面。正好就插入到了4的下一个间隙种。是不被允许的
但是再上图种,(2,1)这样的插入是可以执行的.按照规则会在(1,2)的上面插入。
mysql就是通过间隙锁解决了幻读。但注意以上的条件是必须使用索引和RR隔离级别。
重点:间隙锁主要是为了解决普通索引的数据的一个范围区间被幻读。例如上图num=5.每次在mysql的写操作,或者select的 for update和IN SHARE MODE。都会使用间隙锁。
幻读产生的原因
- 行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录
事务1 where num=5;
事务2 insert into num5(假sql)
那么如果没有间隙锁这个插入会执行成功。造成了幻读。事务1可以是update或者select for update。update where也会检索的。本来按照事务的隔离,事务1的更新或者查询是不能看到事务2的。但这里会看到事务2的新数据。如果num=5并不唯一就是不是唯一索引。会有多条num=5的数据。就需要间隙锁
幻读(phantom read)
- 前提条件:InnoDB引擎,可重复读隔离级别,使用当前读时。
- 表现:一个事务(同一个read view)在前后两次查询同一范围的时候,后一次查询看到了前一次查询没有看到的行。两点需要说明:
1、在可重复读隔离级别下,普通查询是快照读,是不会看到别的事务插入的数据的,幻读只在当前读下才会出现。
2、幻读专指新插入的行,读到原本存在行的更新结果不算。因为当前读的作用就是能读到所有已经提交记录的最新值。
意向锁
插入意向锁,主要作用是每条数据插入的时候都会先检索表中的意向锁,是否和即将插入的数据有冲突。如果没有就直接插入不阻塞,否则阻塞。意向锁的意义就是只需要检索加了意向锁的数据。如果没有将会造成全文检索
自增锁
mysql以前用的是表锁自增,为了让多个事务新增递增有序。共有三种模式。
默认是批量自增。或者不开启。不开启没有办法保证id自增的有序性。批量自增至少保证了一个事务的id有序自增
共享和排他锁
手动输入层面讲for update是排他锁 IN SHARE MODE是共享锁。
间隙锁是共享锁。IN SHARE MODE共享读锁。意向锁也是共享锁
间隙锁和IN SHARE MODE可能会发生死锁。
当两个事务都调用select IN SHARE MODE的时候 有一个想去update 就会死锁。因为IN SHARE MODE不让其他事务修改数据。
那么for update是让查询到的数据只能自己更新。不让别人更新。
select *** lock in share mode适用于只看不写。(自己不写也不让别人写)
forupdate又看又写操作
元数据锁MDL
mysql 5.5加入了MDL事务锁。在不同事务中操作同张表得数据时会进行事务锁进而锁表