Mysql InnoDB 的锁机制

目录

 

前言

1. 锁的分类

1.1 实现方式

1.2 锁的粒度

2. 查询操作加锁方式

2.1 一致性非锁定读

2.2 一致性锁定读

3. 锁的算法

4. 锁的升级

5. 死锁

6.总结


前言

        锁机制的目的是最大程度提高数据库的并发访问,另一方面确保可以以一致的方式读取和修改数据。了解mysql的锁的相关知识,可以在处理涉及数据库的并发操作问题时,有正确的判断和处理方法。打算梳理这些东西的起因是项目组里有一个同事,张口数据被加锁,闭口死锁了,这个问题没法处理,总是推脱,然后自己也说不清原因,可见他对锁的认识并不深入,但我又不好当面直接说驳了他的面子,仅以此文梳理并加以分享,希望有小伙伴在面对类似情况时,可以侃侃而谈,不至于模模糊糊说不清。

1. 锁的分类

1.1 实现方式

        锁机制是用于管理对共享资源的并发访问。InnoDB支持行级锁,按照锁的实现方式,InnoDB实现了两种标准的行级锁:

        共享锁(S Lock):允许事务读取一行数据;

        排他锁(X Lock):允许事务删除或更新一行数据;

        举个例子:如果一个事务t1已经获取了行n的共享锁,那么另一个事务t2可以立即获得行n的共享锁,因为读取操作没有改变行n的数据,所以这种情况叫作锁的兼容()。如果另外有一个事务t3想获得行n的排他锁,那么这时需要等待事务t1、t2释放行n上的共享锁才行,t3等待的过程处于阻塞状态,这种情况就是锁不兼容。

        通过例子可以得出以下结论:

  1.  排他锁与共享锁不兼容;
  2.  排他锁与排他锁不兼容;
  3.  共享锁与共享锁是兼容的;
  4.   共享锁与排他锁不兼容;

        来一个表格更直观一些,另外需要注意的是共享锁与排他锁都是行级锁,兼容是指结同一行数据而言的。

排他锁

共享锁

排他锁

不兼容

不兼容

共享锁

不兼容

兼容

1.2 锁的粒度

        InnoDB存储引擎还支持多粒度的锁定,即允许事务在行级上的锁和表级上的锁同时存在。这种在不同粒度上的锁定称为意向锁,可以将锁定的对象分为多个层次,在更细的粒度上进行加锁的操作,所以意向锁又分为两种实现方式:

        意向共享锁(IS Lock)事务想要获取表中某几行数据的排他锁;

        意向排他锁(IX Lock)事务想要获取表中某几行数据的共享锁;

        举个例子:如果需要对页上的行记录n上加排他锁,那么需要分别对其所在的数据库、表、页上意向锁,最后对行记录n上排他锁。假如在给行记录n上排他锁前,已经有事务给行n所在的表上了共享锁,那么由排他锁与共享锁的不兼容,就需要等待已有的事务结束,共享锁释放后,才能继续。

        需要特别注意的是:共享锁和排他锁锁定的是一行数据,意向共享锁和意向排他锁锁定的是几行,因此意向锁共享锁和意向排他锁不是锁定的相同几行数据,相互之间是锁兼容的。

2. 查询操作加锁方式

        InnoDB查询操作有两种读取方式,本质是两种加锁方式:一致性非锁定读和一致性锁定读。

2.1 一致性非锁定读

        InnoDB默认事务隔离级别为可重复读,InnoDB默认读取方式为一致性非锁定读。举个例子说明一下什么是一致性非锁定读?如果读取数据行n时,行数据n正在执行delete或update操作而被锁定,那么读取的操作不会因此去等待行n上的锁释放,而是直接去读取行n的快照数据,所谓以快照数据就行n锁定之前的版本数据。一致性非锁定锁极大提高了数据库的并发性,所以才是InnoDB默认的读取方式。

        需要特别注意的是,不同的事务隔离级别下读取的数据有区别,这主要是因为不同事务隔离级别下对快照数据的定义不同。因为一条行记录可能有不止一个快照数据,因不同版本并发控制产生的技术,就是传说中的多版本控制(MVCC),但这并不是这里要讲述的重点。

  • 在事务隔离级别为read commited(读已提交),读取被锁定行的最新一份快照数据。
  • 在事务隔离级别为repeatable read,可重复,读取事务开始时的行数据版本。

        这好像也没看出来具体到底有什么区别?别急,举个例子就明白了:

  1. 时间列表示会话操作的时间节点顺序,
  2. 在时间节点1会话1开启事务,在时间节点2执行了查询操作;
  3. 时间节点3时,会话2开启事务,时间节点4执行了一个与时间节点2相同的条件的更新操作,这时id=1的行其实加了一个排他锁;
  4. 时间节点5会话1再执行id=1的查询操作,如果事务隔离级别是读已提交,那么读取被锁定行的最新一份快照数据id是1,因现在会话2的更新操作的事务还未提交,读取已提交的肯定还是id=1; 如果事务隔离级别是可重复读,读取事务开始时的行数据版本也是id=1;所以不管不管事务的隔离级别是读已提交,还是可重复读,结果是一样的。
  5. 时间节点6会话2提交了事务,加在id=1的行上的排他锁释放,在时间节点7再执行查询操作,这时读已提交和可重复读两种事务隔离级别下的结果是不同的。如果是读已提交,由于会话2的更新操作事务已提交,读取最新的一份快照数据id=3了。如果是可重复读,读取事务开始时行数据的版本,由于会话1事务未提交,事务开始时数据的版本是id=1。

时间

会话1

会话2

1

begin

2

select * from table1 where id=1;

3

begin

4

update table1 set id=3 where id=1;

5

select * from table1 where id=1;

6

commit;

7

select * from table1 where id=1;

8

commit;

        通过例子可以了解一个事实:一致性非锁定读在不同的事务隔离级别下读取的结果可能不同,也可能相同,关键在于时机。可以根据实际的场景,显性指定合适的事务的隔离级别。

2.2 一致性锁定读

        InnoDB默认事务隔离级别可重复读时,select查询使用一致性非锁定读,但是用户可以显性对数据库查询操作指定为一致性锁定读。

        两种一致性锁定读的操作:

select ... for update;

select ... lock in share mode;

        select ... for update对读取记录加排他锁,其他事务不能对加排他锁的行上加任何锁,因为锁不兼容;select ... lock in share mode对读取行记录加共享锁,其他事务可以向被锁定的行加共享锁,但不能是排他锁,因为锁不兼容,其事务也会阻塞。

3. 锁的算法

---未完待续,敬请期待

4. 锁的升级

---未完待续,敬请期待

5. 死锁

---未完待续,敬请期待

6.总结

        处理数据并发问题时,绝不能想当然,搞清楚数据库是什么版本存储引擎?事务隔离级别是什么?读取操作加锁方式是什么?这些信息搞清楚,才是分析并发问题的第一步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凡夫贩夫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值