首页 > 数据库 >解决数据重复插入的sql与锁方案

解决数据重复插入的sql与锁方案

时间:2022-12-16 10:37:55浏览次数:40  
标签:索引 重复 表中 插入 user sql WHERE


解决数据重复插入的sql与锁方案

一、问题

在实际应用中,用户可能会有连点操作,当连点操作的时间间隔非常短时,系统很容易造成重复数据的插入,如图所示:

解决数据重复插入的sql与锁方案_数据

可以看到有2条数据被重复插入了(encrytedPassword一致),尽管userId是不同的。

二、解决方案

1.从sql方面解决,这里可以根据数据库表是否有其他唯一索引来划分:

a.表中除了主键外,还有唯一索引时:

假设表中除了主键,存在某一字段a是唯一索引,且已存在于表中的数据为1

  • 使用ignore关键字 如果是用主键primary或者唯一索引unique区分了记录的唯一性,避免重复插入记录可以使用:
INSERT IGNORE into user(a,b,c) VALUES('1','joshua317','13299999999');

插入a=1冲突,这样当有重复记录就会忽略,执行后返回数字0

  • 使用Replace
REPLACE into user(a,b,c) VALUES('1','joshua317','13299999999');

REPLACE的运行与INSERT很相像,但是如果旧记录与新记录有相同的值,则在新记录被插入之前,旧记录被删除

  • 使用ON DUPLICATE KEY UPDATE
INSERT INTO `table` (`a`, `b`, `c`) VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE `c`=`c`+1;

加入a是唯一索引时,如果表中已存在a=1,则插入失败,等价于下面的update语句

UPDATE `table` SET `c`=`c`+1 WHERE `a`=1;

b.表中不存在唯一索引时

上面规定a为表中的唯一索引,但很多时候表中除了id外,是不存在唯一索引的,我们又不能随便改变公司的表结构,所以可以使用下面的方案:

  • 先查询后插入,伪代码:
@Transactional
User register(User user){
User user = UserDao.SelectUserByUid(user.getUid());
if(null==user){
user = UserDao.save(user);
}
return user;
}

这种方法并发时可能会出现错误,比如用户第一次点击时,执行register方法,当前的user查询为null,还没执行save方法时,用户再次点击时,并发执行了register方法,并且完成后,线程回到第一次操作中,再次执行save方法,会导致user重复插入。所以我们要加上**@Transactional**保证事务的原子性。另外这种方法性能稍微差一些

  • **使用insert ignore if not exist,**具体格式为:
INSERT IGNORE INTO 表名('字段') 
SELECT '字段需要插入时的数据' FROM dual
WHERE NOT EXISTS
(SELECT * FROM '表名' WHERE '需要被限制不重复的字段' = '被限制字段本次插入的数据')

比如mybatis使用时为:

INSERT IGNORE INTO user(a,b,c) 
SELECT #{a},#{b},#{c} FROM dual
WHERE NOT EXISTS
(SELECT a,b,c FROM user WHERE b = #{b})

解释一下:

第一行的insert ignore我们知道,插入不进去就取消插入操作

第二行的dual是虚表,select 1,2,3 是合法的,但为了好看,所以变成select xxx from dual虚表

第三行和第四行顾名思义,如果存在b的某个值以及在数据库中存在,那么第一行的insert不生效。

三、悲观锁(分布式锁)方案:

上面虽然是不错的解决方案,但是如果一个工程中所有的save代码都已经写好了,那么都得全部都按照上面的方法改一遍吗?太麻烦了,比如:

解决数据重复插入的sql与锁方案_java_02

另外,如果是分布式呢?一个save方法在多个服务中都存在时,上面的方法 很多时候是不适用的。

所以我们应该从根源解决问题,百因必有果,重复插入操作的根源就是重复执行了这段代码,那我们完全可以使用锁的方式把这段代码锁住,防止重复性操作。

待续解释分布式锁。。。。


标签:索引,重复,表中,插入,user,sql,WHERE
From: https://blog.51cto.com/u_12847493/5946867

相关文章

  • MySQL技术内幕—第一章 体系结构和存储引擎
    MySQL被设计为一个单进程多线程架构的数据库,数据库实例在系统上的表现就是一个进程。当启动实例时,MySQL数据库会去读取配置文件,根据配置文件的参数来启动数据库实例。而......
  • 基于Java+Swing+mysql图书管理系统
    @目录一、系统介绍二、功能展示1.用户登陆2.系统主页3.图书查询4.图书添加5.图书修改6.图书删除7.办理借书8.办理还书9.历史查询10.用户查询、删除(管理员)三、数据库四、其......
  • 基于Java+Swing+mysql图书管理系统 (1)
    @目录一、系统介绍二、功能展示1.用户登陆2.图书管理3.图书添加4.图书类别管理5.图书类别添加三、数据库四、其它1.其他系统实现五、获取源码一、系统介绍程序中实现了登......
  • Java+Swing+mysql员工工资管理系统
    @目录一、系统介绍二、功能展示1.主页2.员工工资查询3.员工工资添加4.员工工资修改5.员工工资删除三、系统实现1.salary.java四、其它1.其他系统实现五、获取源码一、系统......
  • 慢SQL学习记录
    一为什么要做这个事情1什么是慢SQL?这里指的是MySQL慢查询,具体指运行时间超过long_query_time值的SQL。我们常听常见的MySQL中有二进制日志binlog、中继日志relaylog、......
  • 从【各大软件公司笔试压轴题】学习SQL语句
    [size=large]从博客园中看到一篇文章,介绍大软件公司面试时常常会出的两道SQL题(见附录)。我觉得受益很多,在此之前,我一直觉得,SQL2008似乎提供了这方面......
  • MySQL kill进程后出现killed
    一.问题描述拷贝一个大表的表数据的时候,等待时间太久,就在前台通过CTRL+C的方式停掉了   通过showprocesslist查找到对应的进程,然后进行kill,结果kill完了,依旧在进......
  • vivo 在虚拟机linux下插入USB报错 no permission (user in plugdev group)
    https://www.cnblogs.com/lili414/p/16892573.html网上找的解决方法:1. sudovim/etc/udev/rules.d/51-android.rules 创建一个规则文件2.文件中输入如下内容SUBS......
  • ssm报错Could not open JDBC Connection for transaction; nested exception is com.m
    HTTPStatus500-Requestprocessingfailed;nestedexceptionisorg.springframework.transaction.CannotCreateTransactionException:CouldnotopenJDBCConnecti......
  • mysql安装系统错误
    一.看到一篇博客解决二.替换掉增加文件2.1替换位置2.2分享链接链接:https://pan.baidu.com/s/1tig9d7-_HCMYcrIz2SYQpg?pwd=8zzp提取码:8zzp......