mysql的行锁

god-jiang god-jiang     2023-03-23     631

关键词:

背景

自从过年值班就一直在公司读《 MySQL实战45讲》这本书,当时看完了MySQL的全局锁、表级锁还有行锁和间隙锁。其中的行锁和间隙锁迟迟无法理解,最近趁着有空再次阅读了一遍,并且对照了《深入浅出MySQL》,发现对MySQL锁的理解又上了一个台阶,今天就来分享一下MySQL的行锁。

行锁

MySQL的行锁又分为共享锁(S锁)和排他锁(X锁)。

一般普通的select语句,InnoDB不加任何锁,我们称之为快照读

  • select * from test;

通过加S锁和X锁的select语句或者插入/更新/删除操作,我们称之为当前读

  • select * from test lock in share mode;
  • select * from test for update;
  • insert into test values(…);
  • update test set …;
  • delete from test …;

特殊说明:以上的当前读,读取的都是记录的最新版本。对读取记录都会加锁,除了第一条语句lock in share mode是对记录加S锁(共享锁)外,其他的操作都是加X锁(排他锁)

两阶段锁协议

传统的关系型数据库加锁都要遵循一个原则:两阶段锁原则。

两阶段锁是将锁的操作分为两个阶段,加锁阶段和解锁阶段,并且保证加锁阶段和解锁阶段不相交。

分享一个例子说明两阶段锁协议:

事务A事务B
begin;
update test set name=‘god-jiang’ where id=1;
begin;
delete from test where name=‘god-jiang’;
commit;

事务A执行完update语句后,手上持有着name='god-jiang’的X锁,而事务B执行delete语句删除name='god-jiang’记录的时候会阻塞,直到事务A执行commit之后,事务B才会执行delete。

也就是说,在InnoDB存储引擎中,行锁是在需要的时候加上的,但并不是不需要了就立刻释放行锁,而是要等到事务结束的时候才释放,这个就是两阶段锁协议。

事务隔离级别

不同事务隔离级别对应的行锁也是不同的,所以我们需要先了解事务的隔离级别后,再来演示不同隔离级别的行锁如何加上去。

MySQL的4种隔离级别:

  1. READ UNCOMMITTED(读未提交):任何一个事务当中,都可以看见其他事务的执行情况,会出现脏读现象。
  2. READ COMMITTED(读已提交,简称:RC):在当前事务中只能看见已经提交事务的执行结果,当同一事务在读取期间出现新的commit操作,会出现不可重复读现象。
  3. REPEATABLE READ(可重复读,简称:RR):这是MySQL默认的隔离级别,得益于MVCC,它能在同一事务在多实例并发读取数据时看到相同的数据行,消除了脏读、不可重复读,默认不会出现幻读(MySQL的行锁+间隙锁解决了快照读的幻读,未解决当前读的幻读)。
  4. SERIALIZABLE(串行):MySQL的最高隔离级别,通过加锁,强制事务执行顺序,保证不会出现幻读问题。

加锁分析(以下默认是RC隔离级别并且都是当前读)

这里我挑选出RC隔离级别下三种常见情况分析SQL如何加锁:

  • RC隔离级别,where字段没有索引
  • RC隔离级别,where字段有唯一索引
  • RC隔离级别,where字段有普通索引

以下加锁分析默认表名为test,主键为id,唯一索引为a,普通索引为b,无索引为c。

RC隔离级别+where无索引

由于c字段没有索引,SQL将会进行全表扫描。这个时候的所有记录,都会加上X锁。

为什么不是只在c=10的记录上加锁呢?

这是因为在MySQL中,如果where条件不能通过索引快速过滤,那么在MySQL的server层就会将所有记录都加锁然后调用InnoDB存储引擎查询,因此也就把所有记录都锁上了。

总结:没有索引的情况下,InnoDB的当前读会对所有记录都加锁。所以在实际开发中,如果是当前读或者是插入/更新/删除等操作一定要使用索引,否则会产生大量的锁等待

RC隔离级别+where唯一索引

由于a字段有唯一索引,因此通过MySQL的server层会选择走a列的索引进行过滤,找到a=2记录后,将唯一索引上a=2的索引记录加上X锁,同时读取主键id并找到聚集索引树给id=2的记录加上X锁。

总结:如果查询的条件是唯一索引,那么SQL在满足的唯一索引的记录上加X锁,并且在对应的聚集索引上加X锁

RC隔离级别+where普通索引

由于b字段有普通索引,所以在满足b=2的所有记录上都加上了X锁,同时对应的聚集索引记录也加上了X锁。与唯一索引对比,唯一索引查询最多有一行记录上锁,而普通索引会把满足条件的所有记录上锁。

总结:如果查询的条件是普通索引,那么SQL会在满足条件的非唯一索引记录加上X锁,并且会在它们对应的聚集索引上加X锁

总结

  • InnoDB支持行锁,是替代MyISAM存储引擎的重要原因之一
  • 分享了两阶段锁协议和MySQL的事务隔离级别
  • 分析了RC隔离级别下常见的当前读加锁情况

最最重要的就是,在RC隔离级别下,我们更新数据,插入数据,删除数据都要尽可能走索引,不然会使所有的记录都被加上X锁,假如在线上操作的话,会严重影响业务

参考资料

  • 《MySQL实战45讲》林晓斌
  • 《深入浅出MySQL》20.3.4 InnoDB行锁实现方式

mysql中的行锁(代码片段)

1、行锁分类1、记录锁:即锁住记录本身2、间隙锁:锁住一段没有记录的间隙,可以是两条记录的中间部分,也可以是第一条记录的前置部分或最后一条记录的后续部分2.1需要注意的是,间隙锁仅仅是阻塞对... 查看详情

java如何实现对mysql数据库的行锁

场景如下:  用户账户有余额,当发生交易时,需要实时更新余额。这里如果发生并发问题,那么会造成用户余额和实际交易的不一致,这对公司和客户来说都是很危险的。那么如何避免:  网上查了下,有以下两... 查看详情

mysql中innodb引擎的行锁是通过加在啥上完成

...ert一条主键值为1的数据,由于事务A首先获取了主键值为1的行锁,导致事务B因无法获取行锁而产生等待,等到事务A提交后,事务B才获取该行锁,完成提交。这里强调的是行锁的概念,虽然事务B重复插入了主键,但是在获取行... 查看详情

mysql的行锁与表锁(select*....forupdate)

参考技术A  mysql中使用selectforupdate的必须针对InnoDb,并且是在一个事务中,才能起作用。  select的条件不一样,采用的是行级锁还是表级锁也不一样。  由于InnoDB预设是Row-LevelLock,所以只有「明确」的指定主键,MySQL才会... 查看详情

mysql行锁等待异常

...在慢查询,导致同一个事务中的其他DML无法及时释放占用的行锁,引起行锁等待。3.单个事务中包含大量SQL通常是由于在事务代码中加入for循环导致,虽然单个SQL运行很快,但是SQL数量一大,事务就会很慢。4.级联更新SQL执行时... 查看详情

mysql基础篇之行锁--07(代码片段)

...介绍了MySQL的全局锁和表级锁,今天我们就来讲讲MySQL的行锁。MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,... 查看详情

mysql简单介绍——换个角度认识mysql

...数据,索引集中存储,存储于同一个表空间文件中。InnoDB的行锁模式及其加锁方法:InnoDB中有以下两种类型的行锁:共享锁(读锁:允许事务对一条行数据进行读取)和互斥锁(写锁:允许事务对一条行数据进行删除或更新),... 查看详情

day871.行锁-mysql实战(代码片段)

...c;我是阿昌,今天学习记录的是关于行锁的内容。MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引... 查看详情

mysql中innodb的行锁算法

参考技术A众所周知,innodb是默认行锁,当然也支持表锁。如下是对于行锁的算法进行的一些实验。锁的算法为:我知道是行锁,但是是如何锁的,锁多少数据假如有个索引是:[1,2,3,7]recordlock锁的是1,2,3,7gaplock锁的是(-,... 查看详情

mysql的行锁与表锁(select* .... for update)

参考技术A  mysql中使用selectforupdate的必须针对InnoDb,并且是在一个事务中,才能起作用。  select的条件不一样,采用的是行级锁还是表级锁也不一样。  由于InnoDB预设是Row-LevelLock,所以只有「明确」的指定主键,MySQL才会... 查看详情

行锁功过:怎么减少行锁对性能的影响?

MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会... 查看详情

从头开始搞懂mysql(05)行锁表锁全局锁(代码片段)

1、行锁行锁是针对数据表中行记录的锁,MySQL的行锁是在引擎层实现的,并不是所有的引擎都支持行锁,比如MyISAM就不支持,InnoDB支持行锁,避免了并发控制时使用表锁1.1两阶段锁在InnoDB事务中,行锁是在... 查看详情

跑了4个实验,实战讲解mysql的行锁间隙锁...(代码片段)

微信搜索【微观技术】,关注这个不喜欢内卷的程序员。精彩文章汇总GitHubhttps://github.com/aalansehaiyang/technology-talk,Star12K,汇总java生态圈常用技术框架、开源中间件,系统架构、数据库、大公司架构案例、常用三... 查看详情

解释 AWR 报告中的行锁争用

】解释AWR报告中的行锁争用【英文标题】:InterpretingRowLockContentioninAWRReport【发布时间】:2018-11-2819:03:45【问题描述】:我的AWR报告显示如下:EventWaitsTotalWaitTime(s)enq:TX-rowlockcontention3010,69410,694代表时钟时间吗?或者它是否代表在A... 查看详情

关于数据库行锁与表锁的认识(代码片段)

...查询这一条记录却是可以的也就是说虽然这一条记录所在的行被锁定了,但是并不影响我们正常的查询,当然了针对这一行的DML操作也是无效的那如果我们对除了cardid=‘1’的其他行操作会怎样呢?对于其他的行DML是完全... 查看详情

mysql锁--02---行锁(记录锁(recordlocks))(代码片段)

...leslike'tx_isolation';MySQL-行锁InnoDB实现了以下两种类型的行锁。**共享锁(S)**:**排他锁(X)**:InnoDB加锁规则==对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加写锁(排它锁x锁)&... 查看详情

mysql行锁

MySQL行锁场景两阶段锁思考死锁和死锁检测出现死锁后,有两种策略:思考热点数据性能问题小结问题汇总场景行锁是针对数据表中行记录的锁,MySQL行锁是引擎层实现的,不是所有的引擎都支持行锁,MyISAM引... 查看详情

mysql行锁

MySQL行锁场景两阶段锁思考死锁和死锁检测出现死锁后,有两种策略:思考热点数据性能问题小结问题汇总场景行锁是针对数据表中行记录的锁,MySQL行锁是引擎层实现的,不是所有的引擎都支持行锁,MyISAM引... 查看详情