mysql锁技术讨论

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

mysql 常见锁问题分析

1 参考资料

2 要明确的概念

  • 不可重复读和幻读的区别
  • 快照读和当前读
  • 事务的隔离级别
  • record lock、gap lock、next-key lock

2.1 不可重复读和幻读的区别

select数据的不变性可以细分成2部分

  • 第一部分就是:对原有数据的不可修改性,如update delete,通过行锁锁住记录就可以实现不可修改
  • 另一部分就是:对于新增数据的限制,这时候就不能通过行锁来解决了,这时候就需要通过gap lock来解决

如果仅仅满足了第一部分可以叫可重复读,如果也满足了第二部分就算是解决了幻读的问题。

而目前mysql的innodb数据库引擎实现的Repeatable reads不仅仅解决了上述的第一部分也解决第二部分,即Repeatable reads级别下已经解决了幻读问题。

2.2 快照读和当前读

快照读: 如普通的select * from t where id>=6;采用MVCC(多版本并发控制)仅仅读取该事务号及其之前的数据

当前读:如select * from t where id>=6 for update;读取的是最新提交的事务号及其之前的数据

2.3 事务的隔离级别

Read committed 的隔离级别:只能读到别人已提交的数据,未提交的数据读不到,RC的隔离级别存在不可重复读和幻读的现象,即在同一个事务内,第一次select查询出一定结果后,别的客户端此时又修改了源数据和提交了新的数据,第二次select是可以查出修改后的数据和新提交的数据的,这就导致了和第一次select的数据不一致的问题

Repeatable reads 的隔离级别:比起Read committed,解决了不可重复读的现象,而mysql的innodb数据库引擎实现的Repeatable reads也解决了幻读问题。

  • 对于快照读中的幻读(即select * from t where id>=6出现的幻读)采用的解决方式是采用MVCC(多版本并发控制)
  • 对于当前读中的幻读(即select * from t where id>=6 for update出现的幻读)采用的解决方式是gap lock

3 问题分析

如下的一个事务并发执行

start transaction;
DELETE FROM t WHERE id =6;
INSERT INTO t VALUES(6);
commit;

就隔离级别和id唯一索引、id非唯一索引组合等情况展开分析以下内容:

  • 使用了什么锁?阻塞情况?
  • 是否会出现死锁?

3.1 Read committed和唯一索引id

3.2 Read committed和非唯一索引id

3.3 Repeatable reads和唯一索引id

创建表的sql:

create table m (
    id int ,
    primary key (id)
);

填充数据 1、2、6、8

insert into m values(1),(2),(6),(8);

步骤1:客户端A

start transaction;
delete from m where id =6;

步骤2:客户端B

start transaction;
delete from m where id =6;

步骤3:客户端A

insert into m value(6);

步骤4:客户端B

insert into m value(6);

3.3.1 删除一个已存在的值

其中delete from m where id =6语句会对索引中id=6的记录加上next-key lock,但是由于where id=6的查询条件结果是确定的,即不会出现幻读的情况,所以仅仅对id=6的记录加上一个record lock即可,即由next-key lock降级到了record lock。

所以当客户端A执行完毕步骤1后,客户端B执行步骤2的时候,由于已经存在了record lock,所以客户端B会被阻塞,等待客户端A的record lock的释放,现象如下:

输入图片说明

3.3.2 删除一个不存在的值

上述的id=6全部换成id=5,即客户端A执行delete from m where id=5;由于记录不存在,所以只会在索引(2,6)区间中加上gap lock。此时如果客户端B也同样执行delete from m where id=5,由于记录不存在,也只会在索引(2,6)区间中加上gap lock,这两个gap lock之间不冲突,可以同时存在。

此时客户端执行insert into m value(5),因为insert语句会添加一个 insert intention gap lock(见官方文档insert intention gap lock),其中这个锁和gap lock是可以冲突的,此时插入的数值5刚好在上述客户端B创建的gap lock锁定的区间中,所以此时客户端A是要等待客户端B释放gap lock的,即被阻塞了

此时客户端B同样执行insert into m value(5),也会因为客户端A创建的gap lock而造成阻塞,此时客户端A、B相互阻塞造成死锁,现象如下

输入图片说明

发生死锁后,innode引擎自动检测到死锁,会让一个进行释放,另一个得到执行

3.4 Repeatable reads和非唯一索引id

创建表的sql:

create table t (
    id int ,
    key (id)
);

填充数据 1、2、6、8

insert into t values(1),(2),(6),(8);

步骤1:客户端A

start transaction;
delete from t where id =6;

步骤2:客户端B

start transaction;
delete from t where id =6;

步骤3:客户端A

insert into t value(6);

步骤4:客户端B

insert into t value(6);

3.4.1 删除一个已存在的值

其中delete from t where id =6语句会对索引中id=6的记录加上next-key lock,即id=6的记录本身加上record lock,同时(2,6)、(6,8) 这两个区间会加上gap lock。

所以当客户端A执行完毕步骤1后,客户端B执行步骤2的时候,由于已经存在了record lock,所以客户端B会被阻塞,等待record lock的释放,现象如下:

客户端B被阻塞

3.4.2 删除一个不存在的值

假如上述的id=6全部换成id=5,执行 delete from t where id =5的话,即delete 一个不存在的值,则只会对索引的(2,6)区间加上gap lock。客户端B就不会阻塞,同样的在索引(2,6)区间加上gap lock,gap lock之间不冲突的,即这时的客户端B不会阻塞。

这时客户端A执行 insert into t value(5)会阻塞,因为insert语句会添加一个 insert intention gap lock(见官方文档insert intention gap lock),其中这个锁和gap lock是可以冲突的,此时插入的数值5刚好在上述客户端B创建的gap lock锁定的区间中,所以此时客户端A是要等待客户端B释放gap lock的,即被阻塞了

输入图片说明

而客户端A执行的insert into t value(5)所加入的insert intention gap lock是不会和自己创建的gap lock冲突的,即如果没有其他gap lock的话,客户端A往自己创建的gap lock区间中insert值是不被阻塞的

输入图片说明

假如此时客户端B同样执行insert into t value(5)操作,则会因为客户端A创建的gap lock而等待,此时客户端A B在相互等待对方释放gap lock,造成死锁,现象如下:

输入图片说明

发生死锁后,innode引擎自动检测到死锁,会让一个进行释放,另一个得到执行

4 案例演示当前读和快照读

创建表的sql:

create table t (
    id int ,
    key (id)
);

填充数据 1、2、6、8

insert into t values(1),(2),(6),(8);

步骤1:客户端A

start transaction;
select * from t where id>=6;

步骤2:客户端B

start transaction;
insert into t value(10);
commit;

步骤3:客户端A

select * from t where id>=6;
select * from t where id>=6 for update;

步骤3中是第一个select是看不到客户端B新增的数据的,因为他是快照读,读取的是该事务号及其之前的数据

步骤3中的第二个select是可以看到客户端B新增的数据的,因为它是当前读,读取的是最新提交的事务号及其之前的数据

现象如下:

输入图片说明

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
10天前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
34 3
|
1天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
5 1
|
16天前
|
XML 关系型数据库 MySQL
MySQL 导出某些数据的技术详解
MySQL 导出某些数据的技术详解
68 2
|
16天前
|
关系型数据库 MySQL Java
MySQL数据锁:Record Lock,Gap Lock 和 Next-Key Lock
本文基于 MySQL 8.0.30 版本及 InnoDB 引擎,深入解析三种行锁机制:记录锁(Record Lock)、间隙锁(Gap Lock)和临键锁(Next-key Lock)。记录锁锁定索引记录,确保事务唯一修改;间隙锁锁定索引间的间隙,防止新记录插入;临键锁结合两者,锁定范围并记录自身,有效避免幻读现象。通过具体示例展示了不同锁的作用机制及其在并发控制中的应用。
45 2
|
16天前
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
9 1
|
1天前
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
12 0
|
27天前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
134 5
|
3天前
|
关系型数据库 MySQL 数据库
mysql锁详解
通过理解并合理运用MySQL中的锁机制,开发者可以有效管理数据库并发访问,平衡性能与数据一致性需求。更多关于MySQL锁的深入探讨和最佳实践,请参考专业的数据库管理资源[[深入MySQL锁机制详解
7 0
|
1月前
|
存储 关系型数据库 MySQL
技术解析:MySQL中取最新一条重复数据的方法
以上提供的两种方法都可以有效地从MySQL数据库中提取每个类别最新的重复数据。选择哪种方法取决于具体的使用场景和MySQL版本。子查询加分组的方法兼容性更好,适用于所有版本的MySQL;而窗口函数方法代码更简洁,执行效率可能更高,但需要MySQL 8.0及以上版本。在实际应用中,应根据数据量大小、查询性能需求以及MySQL版本等因素综合考虑,选择最合适的实现方案。
226 6
|
17天前
|
关系型数据库 MySQL 数据库
MySQL技术深度解析:每次最大插入条数探秘
MySQL技术深度解析:每次最大插入条数探秘
30 0