号外:为读者持续整理了几份最新教程,覆盖了 Spring Boot、Spring Cloud、微服务架构等PDF。
获取方式:关注右侧公众号"泥瓦匠BYSocket",来领取吧!
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!
目录
为啥要解决数据重复插入?
解决方案实战
可落地小总结
一、为啥要解决数据重复插入?
问题起源,微信小程序抽风 wx.request() 重复请求服务器提交数据。后端服务也很简单,伪代码如下:
class SignLogService {
public void saveSignLog(SignLogDO log){
// 简单插入做记录
SignLogDAO.insert(log);
}
}
发现数据库会存在重复数据行,提交时间一模一样。但业务需求是不能有多余的 log 出现,这明显是个问题。
问题是,重复请求导致的数据重复插入。这问题造成的后果很明显:
数据冗余,可能不单单多一条
有些业务需求不能有多余数据,造成服务问题
问题如图所示:
解决方式:如何将 同请求 A,不执行插入,而是读取前一个请求插入的数据并返回。解决后流程应该如下:
二、解决方案实战
1.单库单表解决方案
唯一索引 + 唯一字段
幂等
上面说的那种业务场景:sign_log 表会有 user_id、sign_id、sign_time 等。那么每次签到,每个人每天只有一条签到记录。
数据库层采取唯一索引的形式,保证数据记录唯一性。即 UNIQUE 约束,UNIQUE 约束唯一标识数据库表中的每条记录。另外,user_id,sign_id,sign_time 三个组合适唯一字段。创表的伪代码如下:
CONSTRAINT unique_sign_log UNIQUE (user_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;
}
}
的确,流量不是很大,也不算很高并发。重复写问题,这样处理即可。那@R_21_403@、高并发场景咋搞
2.分库分表解决方案
a.加锁
// 加锁
jedis.set(lockKey,requestId,"NX",0);">"PX",expireTime);
lockKey 最简单的是 user_id + sign_id + sign_time
expireTime 设置为一天
b.解锁
c.幂等代码加强
三、可落地小总结