关于 MySQL 重复读导致的重复插入问题

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 本文介绍了在开发业务接口时遇到的MySQL重复读导致的数据重复插入问题,并通过伪代码示例详细解析了问题产生的原因。文章提出了四种解决方案:使用共享锁、控制事务并发执行、强制当前读以及调整隔离级别为READ COMMITTED,旨在确保读取最新数据,避免数据重复插入。

背景

昨天在写一个业务接口,遇到 MySQL 重复读导致的重复插入问题,下面是一段伪代码:

js

代码解读

复制代码

async function createClassOrder(uids, classId){
    // 事务开始
    await Promise.all(uids.map(uid => {
        // 将 TBL_CLASS 表进行行锁
        await db.execute('SELECT * FROM TBL_CLASS WHERE id=? FOR UPDATE', [classId])
        // 查找该用户是否已经预约过
        const classOrders = await db.execute('SELECT * FROM TBL_CLASS_ORDER WHERE classId=? AND uid=?', [classId, uid])
        // 如果已经预约过则进行报错
        if(classOrders.length > 0) {
            throw new Error('您已经预约')
        }
        // 创建预约,涉及到表 TBL_CLASS_ORDER
        // 更新课程信息,涉及到表 TBL_CLASS 
    }))
    // 事务结束
}
// 接口路由层有限制重复调用问题

可以发现,这段代码其实在最开始已经有数据库锁了,所以如果涉及到对表 TBL_CLASS 相同行数据进行操作时,事务 A 会进行锁定,事务 B 在执行相同行的时候,会进行等待,直到事务 A 结束,事务 B 再继续执行。但为什么仍然导致数据重复插入呢?原因就在 classOrders 里,当事务 A 结束后,事务 B 继续执行时,因为 MySQL 默认隔离级别是重复读,导致事务 B 在读取 classOrders 时仍然为空。

方案

找到原因,方案就比较容易了,目的就是读取最新数据,无论事务是否提交。

1. 使用共享锁

读取 TBL_CLASS_ORDER 行数据时读取最新数据,可以使用共享锁,例如

js

代码解读

复制代码

const classOrders = await db.execute('SELECT * FROM TBL_CLASS_ORDER WHERE classId=? AND uid=? LOCK IN SHARE MODE', [classId, uid])

2. 事务并发执行

事务 A 结束后再创建事务 B,可以在控制层进行限制。

3. 强制进行当前读

js

代码解读

复制代码

const classOrders = await db.execute('SELECT * FROM TBL_CLASS_ORDER WHERE classId=? AND uid=? FOR UPDATE', [classId, uid])

但可能会导致增加锁竞争

4. 设置隔离级别

设置 READ COMMITTED(读已提交)隔离级别

可以根据业务具体情况进行方案选择。


转载来源:https://juejin.cn/post/7423294364308734004

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
关系型数据库 MySQL 索引
Mysql 大批量数据插入与删除
Mysql 大批量数据插入与删除
57 0
|
6月前
|
关系型数据库 MySQL
【mysql技巧】如何在这个mysql语句执行前加个前提,也就是只有表里没有相同数据才进行添加插入操作
【mysql技巧】如何在这个mysql语句执行前加个前提,也就是只有表里没有相同数据才进行添加插入操作
39 1
|
2月前
|
SQL 监控 关系型数据库
mysql每次最大插入条数
综上所述,虽然MySQL没有严格限制每次插入操作的最大条数,但实际操作中应综合考虑多种因素,采取适当策略来确保数据插入的高效与稳定。
86 0
|
SQL 关系型数据库 MySQL
MySQL防止重复插入(数据库层where方法解决)
MySQL防止重复插入(数据库层where方法解决)
38 0
|
关系型数据库 MySQL Python
一日一技:MySQL批量插入内容但是遇到重复就覆盖
一日一技:MySQL批量插入内容但是遇到重复就覆盖
206 0
|
7月前
|
关系型数据库 MySQL 测试技术
MySQL 并发插入唯一键相邻数据和更新数据导致死锁
一 前言死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案例分析2.1 业务场景业务上的主要逻辑:首先执行插入数据,如果插入成功,则提交。如果插入的时候报唯一键冲突,则执行更新...
243 0
MySQL 并发插入唯一键相邻数据和更新数据导致死锁
|
关系型数据库 MySQL 数据库管理
解决 MySQL 分页数据错乱重复
解决 MySQL 分页数据错乱重复
244 0
|
关系型数据库 MySQL
[MySQL日记]主键唯一键重复插入解决方法
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/51346593 我们插...
1198 0
|
关系型数据库 MySQL
mysql批量插入重复不报错
mysql批量插入重复不报错
143 0
|
关系型数据库 MySQL
mysql如何实现可重复读
mysql基于乐观锁原理实现的mvcc(Multi-Version Concurrency Control,多版本并发控制)
8590 0