Golang如何解决重复提交并发问题

Golang如何解决重复提交并发问题

    • 前言
    • 一 .前端防止重复点击
    • 二 .后端防止重复插入
    • 三. 数据库约束
    • 四 通过事物db.Transaction进行处理
    • 五 redies防重复点击


前言

在实际开发中,有很多情况出现,其中用户重复提交或多个用户同时操作点击同一个server服务提交导致数据冲突脏数据的出现,从而引发问题,解决也比较简单,本文提供四种方法,如下,如有不足还请多多指教


一 .前端防止重复点击

在前端代码中,通过添加点击按钮的禁用状态或设置点击按钮的点击间隔时间来防止用户重复点击提交按钮。这样可以减少用户重复点击的可能性。

二 .后端防止重复插入

在后端代码中,可以通过使用全局锁或分布式锁来确保同一时间只有一个请求可以进行插入操作,从而防止数据的重复插入。下面是使用 sync.Mutex 实现的一个简单示例:

var mu sync.Mutexfunc handleData(data Data) error {mu.Lock()defer mu.Unlock()// 判断数据是否已存在existingData, err := getDataFromDB(data.ID)if err != nil {return err}if existingData != nil {return fmt.Errorf("Data already exists")}// 插入数据到数据库err = insertDataToDB(data)if err != nil {return err}return nil
}

在上述代码中,使用 sync.Mutex 对象 mu 来实现了一个互斥锁。在处理数据的函数中,先获取锁,然后进行数据存在性检查和插入操作,最后释放锁。这样可以确保同一时间只有一个请求可以进行数据插入操作,其他请求会在锁释放后才能进行插入操作。

三. 数据库约束

在数据库中使用唯一约束(Unique Constraint)来防止数据重复插入。在 GORM 中,可以通过在模型定义中使用 gorm:“unique” 标签来设置字段的唯一约束,或者在数据库表中设置唯一索引。这样,当有重复数据插入时,数据库会返回错误,可以捕获该错误并进行相应处理。

type User struct {ID   uint   `gorm:"primaryKey"`Name string `gorm:"unique"`// 其他字段...
}func createUser(user User) error {if err := db.Create(&user).Error; err != nil {if isDuplicateKeyError(err) {// 处理唯一性冲突的错误return fmt.Errorf("User already exists")}// 其他错误处理...return err}return nil
}func isDuplicateKeyError(err error) bool {// 检查错误是否为唯一索引冲突的错误if err != nil {var mysqlError *mysql.MySQLErrorif errors.As(err, &mysqlError) && mysqlError.Number == 1062 {return true}}return false
}

在上述代码中,通过给字段添加 gorm:“unique” 标签,设置字段的唯一约束。然后,在插入数据时,如果发生唯一性冲突,会返回特定的错误,通过检查错误类型和错误代码来判断是否已经存在重复的数据。

四 通过事物db.Transaction进行处理

注意:该方法只针对同一事物中处理多个逻辑,如果是用户点击重复提交,需结合唯一索引或mutex局部锁的方法执行才有效

import ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)type Model struct {ID uint `gorm:"primaryKey"`
}type User struct {ModelName string
}func main() {// 连接数据库dsn := "user:password@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {fmt.Println("数据库连接失败:", err)return}sqlDB, _ := db.DB()defer sqlDB.Close()// 定义事务闭包函数txFunc := func(tx *gorm.DB) error {// 执行插入操作user1 := User{Name: "John"}err := tx.Create(&user1).Errorif err != nil {return err}// 在同一个事务中重复插入相同数据user2 := User{Name: "John"}err = tx.Create(&user2).Errorif err != nil {return err}return nil}// 开始事务err = db.Transaction(func(tx *gorm.DB) error {// 调用事务闭包函数err := txFunc(tx)if err != nil {tx.Rollback()return fmt.Errorf("事务执行失败: %w", err)}return nil})if err != nil {fmt.Println(err)return}fmt.Println("插入成功")
}

五 redies防重复点击

要实现防止重复点击提交功能,可以使用 Redis 实现一个简单的幂等性验证机制。以下是一个示例代码:

import ("fmt""time""github.com/go-redis/redis"
)func main() {// 连接 Redisclient := redis.NewClient(&redis.Options{Addr:     "localhost:6379",Password: "", // 如果需要密码DB:       0,  // 使用默认数据库})// 检查是否已经存在重复提交的标识exists, err := client.Exists("submit_key").Result()if err != nil {fmt.Println("Redis 查询失败:", err)return}if exists == 1 {fmt.Println("请勿重复提交")return}// 设置标识并设置过期时间err = client.Set("submit_key", "submitted", 5*time.Minute).Err()if err != nil {fmt.Println("Redis 设置失败:", err)return}// 执行提交操作err = submit()if err != nil {fmt.Println("提交失败:", err)return}fmt.Println("提交成功")
}func submit() error {// 模拟提交操作time.Sleep(3 * time.Second)return nil
}

在以上示例代码中,我们使用了 Go Redis 客户端库(github.com/go-redis/redis)连接到 Redis,并提供了 Redis 服务器的地址和认证信息(如果需要)。

在提交之前,我们首先检查一个名为 “submit_key” 的键是否存在于 Redis 中。如果存在,说明已经提交过,我们会阻止重复提交。

如果键不存在,我们使用 client.Set 来设置一个键为 “submit_key” 的标识,并设置了过期时间为 5 分钟
在执行具体的提交操作之前,可以根据需要编写自己的提交函数 submit()。
这样,在每次提交之前,先检查 Redis 中的标识是否存在,可以有效防止重复点击提交。

请注意,你需要在本地安装 Redis 服务器,并将地址和认证信息(如果需要)正确配置到示例代码中。同时,你也需要安装 Go Redis 客户端库,可以使用 go get 命令进行安装(如:go get -u github.com/go-redis/redis)。

你可以根据实际需求和业务逻辑进行修改和扩展以上示例代码。

提示:综上所述,可以使用前端和后端的方式来防止用户重复点击导致的数据重复插入问题。同时,也可以利用数据库的唯一约束,或者redies机制来避免数据的重复插入。如有不足或误区,还请多多指教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/600118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FreeRTOS移植

目录 一、FreeRTOS简介1.1 初识FreeRTOS1.2 FreeRTOS资料获取1.3 开发环境简介 二、FreeRTOS移植2.1 文件添加2.2 keil工程添加2.3 文件修改 一、FreeRTOS简介 1.1 初识FreeRTOS 首先看一下 FreeRTOS 的名字,可以分为两部分:“Free”和“RTOS”&#xf…

MySQL基础篇(三)约束

一、概述 概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。 目的:保证数据库中数据的正确、有效性和完整性。 分类: 注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。 二…

智能分析网关V4太阳能风光互补远程视频智能监控方案

一、背景需求 在一些偏远地区,也具有视频监控的需求。但是这类场景中,一般无法就近获取市电,如果要长距离拉取市电,建设的成本非常高且长距离传输有安全隐患,因此风光互补远程视频监控方案的需求也较多。利用风光电转化…

MyBatisPlus学习二:常用注解、条件构造器、自定义sql

常用注解 基本约定 MybatisPlus通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库表信息。可以理解为在继承BaseMapper 要指定对应的泛型 public interface UserMapper extends BaseMapper<User> 实体类中&#xff0c;类名驼峰转下划线作为表名、名为id的…

CentOS 7.6下的HTTP隧道代理配置详解

在CentOS 7.6操作系统中&#xff0c;配置HTTP隧道代理需要一定的技术知识和经验。下面我们将详细介绍如何配置HTTP隧道代理&#xff0c;以确保网络通信的安全性和稳定性。 首先&#xff0c;我们需要了解HTTP隧道代理的基本原理。HTTP隧道代理是一种通过HTTP协议传输其他协议数…

怎么用java写网页?

在Java中&#xff0c;我们通常使用JavaServer Pages (JSP)技术来创建简单的网页。以下是一个简单的JSP页面的示例&#xff1a; jsp复制代码 <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%> <!DO…

Linux习题3

解析&#xff1a; grep&#xff1a;查找文件内的内容 gzip&#xff1a;压缩文件&#xff0c;文件经压缩后会增加 gz&#xff1a;扩展名 find&#xff1a;在指定目录下查找文件 解析&#xff1a; A hosts文件是Linux系统上一个负责ip地址与域名快速解析的文件&#xff0c;以…

gitlab 8.13.0 关闭注册功能

新版本基本都可以在网上找到关闭注册的教程&#xff0c;但是老版本会比较麻烦&#xff0c;可以通过如下路径在网页中设置&#xff08;root 管理员登录&#xff09; ​​​​​​http://ip:port/admin/application_settings 最后保存即可

Java实战项目五:文本冒险游戏

文章目录 一、实战概述二、知识点概览&#xff08;一&#xff09;条件分支与循环结构&#xff08;二&#xff09;面向对象设计&#xff08;三&#xff09;用户交互与事件处理 三、思路分析&#xff08;一&#xff09;系统架构设计&#xff08;二&#xff09;功能模块划分详解 四…

java常见面试题:什么是自动装箱和拆箱(Autoboxing and Unboxing)?

自动装箱和拆箱是Java中的一种特性&#xff0c;它允许基本数据类型和包装类之间自动转换。 自动装箱是指基本数据类型自动转换为对应的包装类对象。例如&#xff0c;当一个基本数据类型数值被赋给一个包装类对象时&#xff0c;Java会自动将这个数值装入对应的包装类中&#xf…

【go语言】select多路选择

select基础知识 select 是 Go 语言中用于处理通道操作的控制结构&#xff0c;它类似于 switch 语句&#xff0c;但专门用于通道的选择。select 语句使得一个 goroutine 可以等待多个通道操作&#xff0c;当其中任意一个通道操作可以进行时&#xff0c;就会执行相应的 case 分支…

Reids原理及简单命令

目录 1.关系数据库与非关系型数据库 关系型数据库 非关系型数据库 关系型数据库和非关系型数据库区别 数据存储方式不同 扩展方式不同 对事务性的支持不同 总结&#xff1a; 2. Redis简介 什么是reids reids优点 reids使用场景&#xff1a; reids快的原因 Redis数…

阿里面试:redis 为什么把简单的字符串设计成 SDS?

面试官:了解redis的String数据结构底层实现嘛? 铁子:当然知道,是基于SDS实现的 面试官:redis是用C语言开发的,那为啥不直接用C的字符串,还单独设计SDS这样的结构呢? 铁子: 其实看得出面试官是想看看,铁子是只停留在redis的使用层面,还是对底层数据结构有过更深入的…

java每日一题——抽红包(答案及编程思路)

前言&#xff1a; 打好基础&#xff0c;daydayup! 题目要求&#xff1a;假设某主播时发起了抢红包活动&#xff0c;五个红包金额分别为9&#xff0c;666&#xff0c;188&#xff0c;520&#xff0c;99999。粉丝按照先来先得&#xff0c;随机抽取&#xff0c;抽完为止&#xff0…

python如何循环读取excel一列中两个单元格之间的数据并写入文本文件?

python如何循环读取excel一列中两个单元格之间的数据并写入文本文件&#xff1f; ━━━━━━━━━━━━━━━━━━━━━━ python如何循环读取excel一列中两个单元格之间的数据&#xff0c;如B2到B22&#xff0c;并写入文本文件&#xff1f; 你可以使用 Python 中的 op…

功能强大且直观的日程和任务管理工具—Things 3 for Mac

在现代生活中&#xff0c;我们面对着繁忙的日程安排和众多的任务&#xff0c;我们需要一款高效的工具来帮助我们管理和组织这些事务。而事务管理的首选工具&#xff0c;非 Things 3 for Mac 莫属。 Things 3 for Mac 是一款功能强大且直观的日程和任务管理工具。它的设计简洁&…

Vue3——element-plus表格组件怎样得到当前行的id

实现方法&#xff1a; <el-table-column property"address" label"操作" show-overflow-tooltip header-align"center" v-slot"scope"><el-button type"success" click"editBtn(scope.row.id)">编辑…

01-线程池项目背景:C++的数据库操作

从0开始学习C与数据库的联动 1.原始方式-使用MySQL Connector/C 提供的API查询 1.1 数据库预操作 我的本地电脑上有mysql数据库&#xff0c;里面预先创建了一个database名叫chat&#xff0c;用户名root&#xff0c;密码password。 1.2 Visual Studio预操作 在Windows上使用…

【unity】Obi插件架构组成(参数详细解释)——解算器四面板设置、三种更新器、参与者介绍

文章目录 一、架构&#xff08;Architecture&#xff09;1.1 Obi解算器&#xff08;ObiSolver&#xff09;1.2 ObiUpdater1.3 ObiActorBlueprint1.4 Obi参与者&#xff08;ObiActor&#xff0c;如ObiRope等&#xff09; 二、Obi解算器&#xff08;ObiSolver&#xff09;2.1 解算…

win11家庭版开启远程桌面功能

win11家庭版不支持远程桌面 下载补丁 https://download.csdn.net/download/yonggeit/88706714 用谷歌浏览器会提示危险文件&#xff0c;选择“保留危险文件”即可&#xff0c;如果大家不放心&#xff0c;可用杀软进行查杀。 解压后 选择“install.bat”右键选择“以管理员身份…