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

目录

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

解决方案实战

可落地小总结

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

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

class SignLogService{

public void saveSignLog(SignLogDO log){

// 简单插入做记录

SignLogDAO.insert(log);

}

}

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

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

数据冗余,可能不单单多一条

有些业务需求不能有多余数据,造成服务问题

问题如图所示:

f84bae2855ecdcd0ceb0eae5335e8c4a.png

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

f2622ca9cfc796adb161893651abea1e.png

二、解决方案实战

1.单库单表解决方案

唯一索引 + 唯一字段

幂等

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

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

CREATE TABLE sign_log

(

id int NOT NULL,

user_id int NOT NULL,

sign_id int,

sign_time int,

CONSTRAINT unique_sign_log UNIQUE (user_id,sign_id,sign_time)

)

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

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

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

class SignLogService{

public SingLogDO saveSignLog(SignLogDO log){

// 幂等处理

SignLogDO insertLog = null;

try {

insertLog = signLogDAO.insert(log);

} catch (DuplicateKeyException e) {

insertLog = selectByUniqueKeys(userId,signId,signTime);

}

return insertLog;

}

}

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

2.分库分表解决方案

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

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

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

a.加锁

// 加锁

jedis.set(lockKey, requestId, "NX", "PX", expireTime);

lockKey 最简单的是 user_id + sign_id + sign_time

expireTime 设置为一天

b.解锁

// 解锁

jedis.eval(script, lockKey,requestId);

c.幂等代码加强

class SignLogService{

public SingLogDO saveSignLog(SignLogDO log){

// 幂等校验

SignLogDO existLog = selectByUniqueKeys(userId,signId,signTime);

if(Objects.nonNull(existLog)) {

return existLog;

}

// 加锁

jedis.set

SignLogDO insertLog = signLogDAO.insert(log);

// 解锁

jedis.eval

return insertLog;

}

}

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

三、可落地小总结

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

幂等:保证多次同意请求后结果一致

并发控制:单表唯一索引、分布式多表分布式锁

降级兜底方案:分布式锁锁失效 – 考虑乐观锁兜底

参考资料

重复插入方案: http://www.bysocket.com/archives/2266

《阿里巴巴 Java 开发手册》

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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值