Mysql的锁

(1)锁类型:表锁&行锁

         表锁是在mysql server层实现的,而行锁的mysql存储引擎实现的

所以表锁相对简单,直接给该表加一个锁标识即可。行锁则相对复杂。

注意事项:

1、innodb加行锁是对主键索引或者二级索引上加锁,而不是对数据加锁。

2、ALTER TABLE 语句锁全表,且不能回滚。

(2)InnoDB 两种类型的行级锁:

共享锁 (也称为 S 锁):允许事务读取一行数据。

可以使用 SQL 语句 select * from tableName where... lock in share mode; 手动加 S 锁。

独占锁 (也称为 X 锁):允许事务删除或更新一行数据。

可以使用 SQL 语句 select * from tableName where... for update; 手动加 X 锁。

       共享锁和排他锁是行锁级别的,对表加锁会遇到一个问题,就是每次加锁之前需要扫描表中的每一行数据,判断该行是不是被锁住,这样比较耗费性能,所有出现了意向锁,就是在对行加锁的时候在表上加一个意向锁,表示该表中有行级锁的存在。意向锁也是表级锁

意向共享锁 (IS):事务即将给表中的各个行设置共享锁,事务给数据行加 S 锁前必须获得该表的 IS 锁。

意向排他锁 (IX):事务即将给表中的各个行设置排他锁,事务给数据行加 X 锁前必须获得该表 IX 锁。

(2)行锁的算法

InnoDB 存储引擎使用三种行锁的算法用来满足相关事务隔离级别的要求。

Record Locks

1、该锁为索引上的锁,如果命中索引,直接加Record锁。

2、当SQL语句无法使用索引时,会进行全表扫描,这个时候MySQL会通过存储引擎给整张表加记录锁,再由MySQL Server层进行过滤,在 MySQL Server 层进行过滤的时候,如果发现不满足 WHERE 条件,会释放对应记录的锁。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。

注意:所以更新操作where条件后面字段尽量走索引,不仅会消耗大量的锁资源,增加数据库的开销,还会极大的降低了数据库的并发性能

Gap Locks

         该锁会锁定一个范围,但是不括记录本身。间隙锁只发生在where条件为索引的情况,才会产生间隙锁 ,如果where 条件不是索引,此时进行update,是会进行表锁。

        假如更新 where id = 49这条记录不存在,这个 SQL 语句还会加锁吗?答案是可能有,这取决于数据库的隔离级别。这种情况下,在 RC 隔离级别不会加任何锁,在 RR 隔离级别会在 id = 49 前后两个索引之间加上间隙锁。

1、间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。

2、这个间隙可以跨一个索引记录,多个索引记录,甚至是空的。

3、使用间隙锁可以防止其他事务在这个范围内插入或修改记录,保证两次读取这个范围内的记录不会变,配合select in share mode或者for update,可以解决幻读。

4、间隙锁和间隙锁之间是互不冲突的

Next-key Locks

       该锁就是 Record Locks 和 Gap Locks 的组合,即锁定一个范围并且锁定该记录本身。InnoDB 使用 Next-key Locks 解决幻读问题

注意:如果id不是主键,而是二级索引,且不是唯一索引,那么这个 SQL 在 RR 隔离级别下就会加如下的 Next-key锁,目的是防止再插入一条相同的索引。


加锁原则

原则 1:加锁的基本单位是 next-key lock。也就是首先更新的时候会先加next-key lock,然后逐渐退化。

原则 2:查找过程中访问到的对象才会加锁。

优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。

优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。

详细可参考:https://www.jianshu.com/p/fb7703906412

说明:

        RR级别下存在幻读的可能,但也是可以使用对记录手动加 X锁 的方法消除幻读。SERIALIZABLE 正是对所有事务都加 X锁 才杜绝了幻读,但很多场景下我们的业务sql并不会存在幻读的风险。SERIALIZABLE 的一刀切虽然事务绝对安全,但性能会有很多不必要的损失。故可以在 RR 下根据业务需求决定是否加锁,存在幻读风险我们加锁,不存在就不加锁,事务安全与性能兼备,这也是RR作为mysql默认隔是个事务离级别的原因,所以需要正确的理解幻读((select ..in share mode)/(select..for update)+间歇锁解决幻读)

其他相关锁:

        AUTOINC锁又叫自增锁(一般简写成 AI 锁),是一种表锁,当表中有自增列(AUTOINCREMENT)时出现。当插入表中有自增列时,数据库需要自动生成自增值,它会先为该表加AUTOINC表锁,阻塞其他事务的插入操作,这样保证生成的自增值肯定是唯一的。

AUTOINC锁具有如下特点:

AUTO_INC锁互不兼容,也就是说同一张表同时只允许有一个自增锁;

[if !supportLists]· [endif]自增值一旦分配了就会+1,如果事务回滚,自增值也不会减回去,所以自增值可能会出现中断的情况。

显然,AUTOINC表锁会导致并发插入的效率降低,为了提高插入的并发性,MySQL从 5.1.22 版本开始,引入了一种可选的轻量级锁(mutex)机制来代替 AUTOINC 锁,可以通过参数innodbautoinclockmode来灵活控制分配自增值时的并发策略。

死锁现象

      死锁 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

InnoDB 引擎采取的是 wait-for graph 等待图的方法来自动检测死锁,如果发现死锁会自动回滚一个事务。

下面我们通过一个示例来了解死锁。

以上是select的时候显式的加排它锁,以上导致的死锁比较明显,好排查,可重复读级别下select不会加锁,但是update会加锁。因为是Innodb隐式的加锁,不太容易排查:

间歇锁导致的死锁:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。