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

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 本文介绍了在开发业务接口时遇到的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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
网络协议 Linux
CentOS7 yum安装报错“Could not resolve host: mirrorlist.centos.org;"之解决办法(换源)
CentOS7 yum安装报错“Could not resolve host: mirrorlist.centos.org; Name or service not known“之解决办法(换源)
|
存储 关系型数据库 MySQL
在 MySQL 中使用 Insert Into Select
【8月更文挑战第11天】
5789 0
在 MySQL 中使用 Insert Into Select
ELK 圣经:Elasticsearch、Logstash、Kibana 从入门到精通
ELK是一套强大的日志管理和分析工具,广泛应用于日志监控、故障排查、业务分析等场景。本文档将详细介绍ELK的各个组件及其配置方法,帮助读者从零开始掌握ELK的使用。
|
JSON 数据处理 数据安全/隐私保护
Ktor库的高级用法:代理服务器与JSON处理
Ktor库的高级用法:代理服务器与JSON处理
|
JavaScript Java Maven
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
7133 1
|
人工智能 自然语言处理 算法
AI 对研发流程的变革
AI编程助手通过自然语言生成代码、解释复杂算法、优化代码等,极大提升了开发效率与代码质量。开发者可利用通义灵码进行代码解释、生成注释及单元测试,简化开发流程。在需求分析、设计、编码、测试到部署的全流程中,AI助手表现优异,尤其在编码和测试阶段显著提高工作效率。尽管目前AI助手在需求分析方面尚需改进,但其未来发展潜力巨大,有望逐步替代部分人力工作。体验地址:[阿里云智能编码](https://www.aliyun.com/solution/tech-solution/intelligent-coding)。
|
关系型数据库 数据库 数据安全/隐私保护
springboot+dynamic-datasource多数据源配置动态切换
springboot+dynamic-datasource多数据源配置动态切换
5344 0
|
设计模式 缓存 Devops
微服务架构最强讲解,那叫一个通俗易懂!
微服务架构(Microservice Architecture)是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。你可以将其看作是在架构层次而非获取服务的
33368 3
微服务架构最强讲解,那叫一个通俗易懂!
|
API Python
Python Web框架:Django、Flask和FastAPI巅峰对决
Python Web框架:Django、Flask和FastAPI巅峰对决
14510 1
|
前端开发 JavaScript
CSS进阶-CSS选择器高级:伪类与伪元素
【6月更文挑战第13天】本文探讨了CSS伪类与伪元素的核心概念,包括伪类表示元素状态,伪元素创造抽象内容。常见问题涉及二者区别、冒号使用、顺序优先级及`content`属性。实践技巧涵盖`:not()`选择器、`:hover`与子元素伪类结合及自定义形状。通过代码示例展示了高亮悬停行、添加图标、首行样式和链接颜色的应用。理解并熟练运用伪类和伪元素可提升CSS设计效率和灵活性。
517 2
CSS进阶-CSS选择器高级:伪类与伪元素