MySQL 插入记录,主键索引冲突加什么锁?
这一期开始进入锁的实战篇,结合原理分析各种场景的加锁情况。
>作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。 > >爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
1. 准备工作
创建测试表:
CREATE TABLE `t1` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `i1` int DEFAULT '0', PRIMARY KEY (`id`) USING BTREE, KEY `idx_i1` (`i1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
插入测试数据:
INSERT INTO `t1` (`id`, `i1`) VALUES (10, 101), (20, 201), (30, 301), (40, 401);
2. 加锁情况
t1 表中已经有一条 <id = 10> 的记录,我们执行以下 insert 语句,再插入一条 <id = 10> 的记录。
begin; insert into t1(id, i1) values (10, 1010);
因为新插入记录和表中原有记录存在主键冲突,执行 insert 语句之后,报错如下:
(1062, "Duplicate entry '10' for key 't1.PRIMARY'")
执行以下 select 语句查询加锁情况:
select engine_transaction_id, object_name, lock_type, lock_mode, lock_status, lock_data from performance_schema.data_locks where object_name = 't1' and lock_type = 'RECORD'\G ***************************[ 1. row ]*************************** engine_transaction_id | 247910 object_name | t1 lock_type | RECORD lock_mode | S,REC_NOT_GAP lock_status | GRANTED lock_data | 10
lock_data = 10, lock_mode = S,REC_NOT_GAP 表示对 <id = 10> 的记录加了共享普通记录锁。
3. 原理分析
insert 语句执行过程中,插入记录到主键索引之前,需要先找插入记录的目标位置。
目标位置为表中主键字段值小于等于新插入记录中主键字段值的最后一条记录之后。
以示例 SQL 为例,插入主键字段 <id = 10> 的记录。
插入记录到主键索引之前,先找到表中 id 小于等于 10 的最后一条记录,也就是 <id = 10, i1="101">
这条记录。新插入记录的目标位置就是这条记录之后。
InnoDB 发现表中已经有一条 <id = 10> 的记录,现在又要插入一条 <id = 10> 的记录,可是主键索引中不允许存在重复记录,这可怎么办才好?
直接报错吗?
那样简单粗暴就过于武断了。
InnoDB 还需要对表中 <id = 10> 的记录验明正身,确定它是一条有效的记录。
如果表中 <id = 10> 的记录已经被标记删除,只是还没有被清理,它就不是有效的记录了。这种情况下,新记录可以正常插入,不会报错。
否则,新记录和表中已有记录冲突,不能插入,就可以报错了。
为了防止其它事务更新或者删除这条记录,检查表中记录是否有效之前,InnoDB 会对这条记录加共享普通记录锁。
这就是示例 SQL 执行过程中对 <id = 10> 的记录加共享普通记录锁的原因。
如果表中 <id = 10> 的记录已经被标记删除,但是删除这条记录的事务还没有提交怎么办?
那我们看到的加锁情况就不一样了。
我们可以模拟下这个场景,创建 2 个 MySQL 连接,分别执行 delete 语句和 insert 语句。
-- 连接 1(事务 1) begin; delete from t1 where id = 10; -- 连接 2(事务 2) begin; insert into t1(id, i1) values (10, 1010);
然后执行以下 select 语句查看加锁情况:
select engine_transaction_id, object_name, lock_type, lock_mode, lock_status, lock_data from performance_schema.data_locks where object_name = 't1' and lock_type = 'RECORD'\G ***************************[ 1. row ]*************************** engine_transaction_id | 247916 object_name | t1 lock_type | RECORD lock_mode | S,REC_NOT_GAP lock_status | WAITING lock_data | 10 ***************************[ 2. row ]*************************** engine_transaction_id | 247911 object_name | t1 lock_type | RECORD lock_mode | X,REC_NOT_GAP lock_status | GRANTED lock_data | 10
事务 247911 执行删除操作对 <id = 10> 的记录加了排他普通记录锁。
事务 247916 想要对 <id = 10> 的记录加共享普通记录锁被阻塞,进入等待状态。
4. 总结
没有需要总结的内容了。
但是有两个问题:事务 247911 提交或者回滚之后,加锁情况是什么样的?为什么会这样?
欢迎大家评论区留言交流。
更多技术文章,请访问:https://opensource.actionsky.com/
关于 SQLE
SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。
✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource
🔗 商业支持:https://www.actionsky.com/sqle</id></id></id></id></id></id></id></id></id></id></id></id></id>

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
回顾 Oracle 在 MySQL 8.0 中的管理工作
对于新兴技术而言,社区管理并不难。经过 29 年的发展,MySQL 已成为全球数百万用户最常使用和最信赖的开源数据库之一。管理如此规模的社区可能非常复杂。我们努力在稳定性和创新性之间找到平衡,为客户提供稳定且可预测的平台,并为技术用户创建新功能。Oracle 通过投资于工程、社区和市场对该技术的采用,持续提供对 MySQL 社区的管理和领导。 作者:Frederic Descamps,EMEA 和亚太地区的 MySQL 社区经理。于 2016 年 5 月加入 MySQL 社区团队。担任开源和 MySQL 顾问已超过 15 年。最喜欢的主题是高可用和高性能。 本文和封面来源:https://blogs.oracle.com/,爱可生开源社区翻译。 本文约 1800 字,预计阅读需要 6 分钟。 引言 对于新兴技术而言,社区管理并不难。经过 29 年的发展,MySQL 已成为全球数百万用户最常使用和最信赖的开源数据库之一。管理如此规模的社区可能非常复杂。我们努力在稳定性和创新性之间找到平衡,为客户提供稳定且可预测的平台,并为技术用户创建新功能。Oracle 通过投资于工程、社区和市场对该技...
- 下一篇
Torna v1.29.3 发布,企业接口文档解决方案
Torna v1.29.3 已经发布,企业接口文档解决方案。 此版本更新内容包括: 【修复】非管理员无法生成代码问题 【修复】LOGO区域不显示版本号问题 详情查看:https://gitee.com/durcframework/torna/releases/v1.29.3
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6