摘要:问题是,重复请求导致的数据重复插入。这问题造成的后果很明显数据冗余,可能不单单多一条有些业务需求不能有多余数据,造成服务问题问题如图所示解决方式如何将同请求,不执行插入,而是读取前一个请求插入的数据并返回。那么使用分布式锁的解决方案。
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!
目录
为啥要解决数据重复插入?
解决方案实战
可落地小总结
一、为啥要解决数据重复插入?问题起源,微信小程序抽风 wx.request() 重复请求服务器提交数据。后端服务也很简单,伪代码如下:
class SignLogService { public void saveSignLog(SignLogDO log) { // 简单插入做记录 SignLogDAO.insert(log); } }
发现数据库会存在重复数据行,提交时间一模一样。但业务需求是不能有多余的 log 出现,这明显是个问题。
问题是,重复请求导致的数据重复插入。这问题造成的后果很明显:
数据冗余,可能不单单多一条
有些业务需求不能有多余数据,造成服务问题
问题如图所示:
解决方式:如何将 同请求 A,不执行插入,而是读取前一个请求插入的数据并返回。解决后流程应该如下:
唯一索引 + 唯一字段
幂等
上面说的那种业务场景: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/archi...
《阿里巴巴 Java 开发手册》
以下专题教程也许您会有兴趣《Spring Boot 2.x 系列教程》
《Java 核心系列教程》
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/77619.html
摘要:小明马上开发完毕,成功上线。下班过后,小明回想大红说的话,什么是间隙锁,什么是插入意向锁,看来作为开发者对数据库不应该只会写啊,不然遇到一些疑难杂症完全没法解决啊。破坏了数据库中的隔离性。 1.锁? 1.1何为锁 锁在现实中的意义为:封闭的器物,以钥匙或暗码开启。在计算机中的锁一般用来管理对共享资源的并发访问,比如我们java同学熟悉的Lock,synchronized等都是我们常见的...
阅读 3249·2023-04-25 22:47
阅读 3764·2021-10-11 10:59
阅读 2299·2021-09-07 10:12
阅读 4242·2021-08-11 11:15
阅读 3431·2019-08-30 13:15
阅读 1749·2019-08-30 13:00
阅读 968·2019-08-29 14:02
阅读 1679·2019-08-26 13:57