此文为转载,先说一个我在生产上真实存在的案例

业务场景:我们做的是仓库管理系统,产线上需要零件的时候,首先生成一个拉动,我需要把拉动,生成拣货单,然后根据拣货单去拣货,上线。

操作流程是:在web上选择多个拉动单,点击组单按钮,即可生产拣货单。

但是存在这样一个问题:原来拉动单:拣货单 1:1,结果一个拉动单生成了多个拣货单,且生成的拣货单创建时间完全一样,但是操作人,不完全一样。

分析原因:

1,一个人连续点击组单按钮,导致重复请求后台接口。

2,两个人在不同电脑同时组单,也就是存在并发操作,这也是为什么生成拣货单的创建人不同。

解决方案:

1,前端控制:针对原因1,前端可做防重复提交控制。

2,加锁:使用redis对拉动单加锁,过滤请求的拣货单。即先对请求参数校验,判断是否已加锁,是过滤掉,否保存并加锁。

3,唯一索引:对拣货单中拉动单号加唯一索引,如果已经组过单,报错,写入失败。

3,幂等:新增之前,根据拉动单号查询拣货单,存在就不新增。

但是通过上面的控制,一定程度上减少的重复组单的数据,后来在生产上还有重复组单的情况。

至今不知道原因,和解决方案,请看到此篇文章的大神指点

qq:1941599064

电话:18298182575

--------------------------------------------------以下为装载内容----------------------------------------

解决方案:如何防止数据重复插入?

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!

目录

  1. 为啥要解决数据重复插入?
  2. 解决方案实战
  3. 可落地小总结

一、为啥要解决数据重复插入?

问题起源,微信小程序抽风 wx.request() 重复请求服务器提交数据。后端服务也很简单,伪代码如下:

1 class SignLogService {
2     public void saveSignLog(SignLogDO log) {
3         // 简单插入做记录
4         SignLogDAO.insert(log);
5     }
6 }

发现数据库会存在重复数据行,提交时间一模一样。但业务需求是不能有多余的 log 出现,这明显是个问题。

问题是,重复请求导致的数据重复插入。这问题造成的后果很明显:

  • 数据冗余,可能不单单多一条
  • 有些业务需求不能有多余数据,造成服务问题

问题如图所示:

解决方式:如何将 同请求 A,不执行插入,而是读取前一个请求插入的数据并返回。解决后流程应该如下:

二、解决方案实战

1.单库单表解决方案

  • 唯一索引 + 唯一字段
  • 幂等

上面说的那种业务场景:sign_log 表会有 user_id、sign_id、sign_time 等。那么每次签到,每个人每天只有一条签到记录。

数据库层采取唯一索引的形式,保证数据记录唯一性。即 UNIQUE 约束,UNIQUE 约束唯一标识数据库表中的每条记录。另外,user_id,sign_id,sign_time 三个组合适唯一字段。创表的伪代码如下:

1 CREATE TABLE sign_log
2 (
3 id int NOT NULL,
4 user_id int NOT NULL,
5 sign_id int,
6 sign_time int,
7 CONSTRAINT unique_sign_log UNIQUE (user_id,sign_id,sign_time)
8 )

重点是 CONSTRAINT unique_sign_log UNIQUE (user_id,sign_id,sign_time)。有个小问题,数据量大的时候,每条记录都会有对应的唯一索引,比较耗资源。那么这样就行了吗?

答案是不行,服务不够健壮。第一个请求插入成功,第二个请求直接报错,Java 服务会抛出 DuplicateKeyException 。

简单的幂等写法操作即可,伪代码如下:

01 class SignLogService {
02     public SingLogDO saveSignLog(SignLogDO log) {
03         // 幂等处理
04         SignLogDO insertLog = null;
05         try {
06             insertLog = signLogDAO.insert(log);
07         } catch (DuplicateKeyException e) {
08             insertLog = selectByUniqueKeys(userId,signId,signTime);
09         }
10  
11         return insertLog;
12     }
13 }

的确,流量不是很大,也不算很高并发。重复写问题,这样处理即可。那大流量、高并发场景咋搞

2.分库分表解决方案

流量大了后,单库单表会演变成分库分表。那么基于单表的唯一索引形式,在碰到分表就无法保证呢,插入的地方可能是两个分表 A1 和 A2。

解决思路:将数据的唯一性条件放到其他存储,并进行锁控制

还是上面的例子,每天,每次签到,每个人只有一条签到记录。那么使用分布式锁 Redis 的解决方案。大致伪代码如下:

a.加锁

1 // 加锁
2 jedis.set(lockKey, requestId, "NX", "PX", expireTime);
  • lockKey 最简单的是 user_id + sign_id + sign_time
  • expireTime 设置为一天

b.解锁

1 // 解锁
2 jedis.eval(script, lockKey,requestId);

c.幂等代码加强

01 class SignLogService {
02     public SingLogDO saveSignLog(SignLogDO log) {
03  
04         // 幂等校验
05         SignLogDO existLog = selectByUniqueKeys(userId,signId,signTime);
06         if(Objects.nonNull(existLog)) {
07             return existLog;
08         }
09  
10         // 加锁
11         jedis.set
12  
13         SignLogDO insertLog = signLogDAO.insert(log);
14  
15         // 解锁
16         jedis.eval
17  
18         return insertLog;
19     }
20 }

这个方案还是不是很成熟,大家参考下即可。

三、可落地小总结

解决方案实战中,了解具体术。归纳如下:

  • 幂等:保证多次同意请求后结果一致
  • 并发控制:单表唯一索引、分布式多表分布式锁
  • 降级兜底方案:分布式锁锁失效 – 考虑乐观锁兜底

参考资料

  • 重复插入方案: http://www.bysocket.com/archives/2266
  • 《阿里巴巴 Java 开发手册》

以下专题教程也许您会有兴趣

  • 《Spring Boot 2.x 系列教程》
  • 《Java 核心系列教程》

解决方案:如何防止数据重复插入相关推荐

  1. 解决数据重复插入问题(sql与锁方法)

    解决数据重复插入(sql与锁方法) 一.问题 在实际应用中,用户可能会有连点操作,当连点操作的时间间隔非常短时,系统很容易造成重复数据的插入,如图所示: 可以看到有2条数据被重复插入了(encryte ...

  2. 数据重复插入问题及解决方案

    本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/25 1.问题背景 某天客户反馈说,有一条一模一样的数据 ...

  3. 灵活运用分布式锁解决数据重复插入问题

    作者:快应用服务器研发团队-Lin Yupan 一.业务背景 许多面向用户的互联网业务都会在系统后端维护一份用户数据,快应用中心业务也同样做了这件事.快应用中心允许用户对快应用进行收藏,并在服务端记录 ...

  4. 并发下,使用redis防止数据重复插入(数据库未对表字段设置唯一情况下)

    2019独角兽企业重金招聘Python工程师标准>>> @Controller @RequestMapping("/myTest") public class T ...

  5. oracle插入数据不重复,oracle插入数据重复

    版本号: 业务场景(如下): 后台自动执行的SQL插入数据,出现了重复数据,且无法稳定重现,偶然会遇见一次.场景为一组数据,如10条,依次插入数据库中.采取了循环生成sql拼接,先删后插的方式,最后统 ...

  6. mysql用唯一约束避免重复_mysql中创建唯一约束防止数据重复

    针对数据重复插入的情况,我们通常会在业务代码中进行处理,就是说入库的时候先查一遍有没有,没有记录的情况再准许入库.但是如果只是自己处理业务代码时先查后入库,并发高时会发生意想不到的后果. 比如现在表t ...

  7. 多次请求事务未提交导致的数据重复入库问题

    起因 最近的一次开发中,请求过来要判断数据是否存在,不存在则入库.由于是循环有着相同的数据入库请求,在执行时上一次事务还未提交,第二次的请求在数据库查询判断时发现数据不存在,又执行了一次入库请求,导致 ...

  8. SQL 语句之insert语句插入数据:若表中有重复的主键或数据继续插入解决方案

    已知条件:MySQL数据库  存在一张表,表名为teacher,主键为id,表中有4行数据 select * from teacher; 要求:要求使用数据库插入语句往表中插入数据,若需要插入表中的数 ...

  9. mysql如何防止插入重复数据_防止MySQL重复插入数据的三种方法

    新建表格 CREATE TABLE `person` ( `id` int NOT NULL COMMENT '主键', `name` varchar(64) CHARACTER SET utf8 C ...

  10. 的table数据重复添加_Mysql的奇技淫巧 - 避免重复插入数据

    Mysql的奇技淫巧 - 避免重复插入数据 MySql 避免重复插入数据 通常那我们插入一条带有唯一性字段数据的时候,我们先去查一下数据库有没有相同数据,需要操作2次数据库,总感觉这样做特别的low, ...

最新文章

  1. spring-boot-mybatis
  2. 关于MVP模式的一些讨论文章
  3. 分析函数调用的汇编指令
  4. Go丨语言学习笔记--func
  5. VR: AR和VR演进哲学
  6. python怎么设置颜色深浅变化_python之深浅拷贝
  7. my CSAPP Attack lab堆栈详解
  8. PWA(Progressive Web App)入门系列:Push
  9. 前端学习(568):元素定高 容器定高 为什么不能居中
  10. 微软 2006年7月已试发布 ERP Dynamics AX 简体中文版 4.0 (第一个简体中文版),请下吧 !...
  11. 在linux中,boot与uboot有什么区别?
  12. 安装openstack(pike版本)nova节点,yum安装报错分析
  13. ios模拟器装ipa包_给iOS 模拟器“安装”app文件
  14. 【经验分享】U盘软刷映泰TB250-BTC刷魔改BIOS上6789代CPU,另解决开机转一下后停止问题
  15. 网络对抗技术_实验四_恶意代码技术
  16. 爬取京东商品信息出现登陆页面的解决
  17. python项目实战(二):选课系统(采用面向对象思想开发)
  18. YOLOV3在windwos下的配置和训练过程简述
  19. Android百度地图屏蔽油站,怎么用android百度地图api获取离当前位置最近的加油站...
  20. 区分有状态和无状态组件

热门文章

  1. 防火防盗竟防不了网络攻击 智能摄像机的网络安全怎么办?
  2. git fork的项目与原作者同步
  3. python一个简单的一元二次方程求解的过程
  4. [改善Java代码]边界,边界,还是边界
  5. 2015 年 4 月份 LeanCloud 更新汇总
  6. 用思科2610搭建PSTN拨号网络
  7. 传输层协议(8):滑动窗口(2)
  8. PostgreSQL 角色与用户管理介绍
  9. mod_shout 模块
  10. Linux 设备驱动--- 并发 与 竞态 --- atomic_t --- atomic_dec_and_test --- 原子操作