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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 数据重复插入问题及解决方案

一、引起数据重复的原因:

引起数据库被重复插入的原因无外乎这几个,表象原因可能是网络延迟、用户连点、高并发等等,实际上是我们在数据库设计、代码逻辑的严谨性上出了问题。

网络延迟、用户连点:用户填写完表单,然后点击提交按钮,由于网络延迟,迟迟得不到后台的响应,这时用户会下意识的再次点击,这就导致了用户的连点,从而导致后端再第一次数据没处理完的时候,又进行了第二次提交,导致数据重复。

 

高并发场景:同个时间点,提交大量相同请求。

 

二、解决数据重复的方案(幂等性):

幂等:在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

1. 前端解决方案(不可靠,推荐)

前端解决办法就是:用户点击按钮后,让按钮点击失效或者禁用,待后端响应完成后,按钮恢复可用状态。

2. 数据库解决方案(可靠,推荐,适合高并发场景)

(1)在数据库中添加唯一索引或唯一约束:幂等主要手段就是通过表中的唯一约束实现,所以可以在需要去重的列上添加唯一索引,这样在插入数据时,重复数据会引发唯一索引冲突的错误,从而避免插入相同数据。

(2)在代码中使用集合去重:可以在代码中使用集合对要插入的数据进行去重操作,然后将去重后的数据插入数据库。例如,使用 Java 中的 HashSet,由于 HashSet 会自动去重,因此可以将数据插入 HashSet 中,再将去重后的数据插入数据库。

(3)在应用程序中进行去重(不推荐)在插入数据之前,可以查询数据库中是否已经存在相同数据。如果存在相同数据,可以选择更新已有数据或者直接跳过插入操作。(不推荐先查询再插入的方案,主要是因为性能效率不好,以及在高并发场景下并不能满足需求。)

(4)使用 ignore 关键字:如果是用主键 primary 或者唯一索引 unique 区分了记录的唯一性,避免插入重复记录可以使用 IGNORE 关键字,这样当有重复记录时就会忽略插入,执行后返回数字 0:IGNORE 只关注主键对应记录是不存在,无则添加,有则忽略。

INSERT IGNORE into user VALUES('1','joshua317','13299999999');

3. 悲观锁解决方案(可靠,推荐,适合高并发场景)

悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;

特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;

加解锁伪代码如下:

/***

1、 客户端 A 请求服务器设置 key 的值,如果设置成功就表示加锁成功

2、 客户端 B 也去请求服务器设置 key 的值,如果返回失败,那么就代表加锁失败

3、 客户端 A 执行代码完成,删除锁

4、 客户端 B 在等待一段时间后在去请求设置 key 的值,设置成功

5、 客户端 B 执行代码完成,删除锁

**/

$lockKey = 'lock_key';

$lockVaule = 'lock_vaule';

$isLock = $redisHandle->set ($lockKey, $lockVaule, ['nx', 'ex' => $ttl]);//nx 代表当 key 不存在时设置 ex 代表设置过期时间

if ($isLock){// 1. 获取锁成功

 

   // 2. 处理业务

 

   // 3. 解锁

   $redisHandle->del($lockKey);

} else {

   // 资源被其他请求占用,提示服务繁忙,请稍后再试

}

意外情况发生

  1. 假设锁提前过期后,客户端 A 还没执行完,然后客户端 B 获取到了锁,这时候客户端 A 执行完了,会不会在删锁的时候把 B 的锁给删掉?

$lockKey = 'lock_key';

$lockVaule = 'lock_key_'.uniqid ();  // 分配一个随机值

$isLock = $redisHandle->set ($lockKey, $lockVaule, ['nx', 'ex' => $ttl]);//nx 代表当 key 不存在时设置 ex 代表设置过期时间

if ($isLock) {// 1. 获取锁成功

   if ($redisHandle->get ($lockKey) == $value) {  // 防止提前过期,误删其它请求创建的锁

       // 2. 处理业务

 

       // 3. 解锁

       $redisHandle->del($lockKey);

   } else {

       // 资源被其他请求占用,提示服务繁忙,请稍后再试

   }

} else {

   // 资源被其他请求占用,提示服务繁忙,请稍后再试

}

2. 假设客户端业务处理中断,解锁失败导致锁没有释放且过期时间未到,然后客户端 B 却无法获取锁进行处理呢?

$lockKey = 'lock_key';
$lockVaule = 'lock_key_'.uniqid ();  // 分配一个随机值
try {
   $isLock = $redisHandle->set ($lockKey, $lockVaule, ['nx', 'ex' => $ttl]);//nx 代表当 key 不存在时设置 ex 代表设置过期时间
   if ($isLock) {// 1. 获取锁成功
       if ($redisHandle->get ($lockKey) == $value) {  // 防止提前过期,误删其它请求创建的锁
           // 2. 处理业务

           // 3. 解锁
           $redisHandle->del($lockKey);
       } else {
           // 资源被其他请求占用,提示服务繁忙,请稍后再试
       }
   } else {
       // 资源被其他请求占用,提示服务繁忙,请稍后再试
   }
} catch (Exception $e) {
   // 3. 解锁
   $redisHandle->del($lockKey);
}

参考:数据重复插入问题及解决方案

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5月前
|
SQL 关系型数据库 MySQL
数据的插入和查询
数据的插入和查询
|
12月前
442. 数组中重复的数据
442. 数组中重复的数据
|
关系型数据库 MySQL 索引
新增数据时,MySQL索引树的自调整过程
刚开始你一个表建好后,就一个数据页,就是聚簇索引的一部分,而且还是空的。若你插入数据,就是直接往这数据页里插入,也没必要给他弄索引页
114 0
|
数据库 数据安全/隐私保护
批量插入【对新数据进行插入操作,已存在的记录进行更新操作】
批量插入【对新数据进行插入操作,已存在的记录进行更新操作】
157 0
批量插入【对新数据进行插入操作,已存在的记录进行更新操作】
删除一段时间内的记录,关键在于删除时筛选条件确定删除范围
删除一段时间内的记录,关键在于删除时筛选条件确定删除范围
94 0
|
算法 前端开发
数组中重复的数据
🎈每天进行一道算法题目练习,今天的题目是“数组中重复的数据”。
166 0
|
数据库 索引
存在逻辑删除的表字段上建立唯一索引的巧办法 (逻辑删除与唯一索引)
设计数据库唯一索引时,经常会碰到唯一删除的键值,导致很难处理,这里就简单介绍一种巧办法,帮你快速解决该问题
1981 0
存在逻辑删除的表字段上建立唯一索引的巧办法 (逻辑删除与唯一索引)
|
Java C语言
练习6—数据插入
练习6—数据插入
109 0
|
SQL 关系型数据库 MySQL
MySql批量大数据插入时,如何不插入重复的数据?
MySql批量大数据插入时,如何不插入重复的数据?
450 0
MySql批量大数据插入时,如何不插入重复的数据?
|
小程序 PHP
解决往数组添加数据,第二次会覆盖第一次的方案
解决往数组添加数据,第二次会覆盖第一次的方案
322 0