最近在业务中出现了数据重复的问题,通过简单的查询发现数据都是在极短的时间片内同时插入的,属于高并发下数据重复的问题。接下来从前后端两个方向寻求解决方案。
前端
通过jquery为元素绑定事件、解绑事件来达到避免前端页面多次点击、多次请求的情况出现。
1.一般在页面加载或者对话框弹出时为业务按钮(或对话框内的业务按钮)绑定点击事件:
$("#btn").on("click", {"paramA": "a"}, saveData);
“这里的
{"paramA": "a"}是函数saveData所携带的参数。在函数中通过event.data.paramA来获取值。
2.当点击发生后,在点击事件方法中第一步就解绑该事件:
$("#btn").off("click", saveData);
这样一来短时间内的多次点击只有第一次能触发点击事件,这就避免了数据重复的问题。当业务流程执行完毕且有正确的返回值后再依据业务需求决定是否需要再次绑定点击事件;而业务流程失败后必须重新绑定点击事件。
“针对弹出对话框时绑定事件的情况,如果弹框后不进行任何操作,必须在关闭对话框后同时解绑事件,否则用户多次打开关闭对话框,会为业务按钮绑定多次点击事件,当下一次点击时就会同时发出N次请求。
后端
有时候前端的方案并不能完美解决数据重复问题,还需要在服务器端进行处理,处理方法很多,这里选择利用redis的setnx(setIfAbsent)的原子性属性避免高并发下的数据重复插入。
// 获取数据的唯一标识符作为 key 设置到 Redis 中
// 如果 Redis 中已存在 key 则返回 false,否则返回 true
Boolean redisSetnx = redisConfig.getRedisTemplate().opsForValue().setIfAbsent(key, value);
if(redisSetnx) {
// 表示 key 正常插入 Redis 中,非重复数据
// 为 key 设置过期时间,在该时间内重复数据都不会进入此代码块中
redisConfig.getRedisTemplate().expire(key, 10, TimeUnit.SECONDS);
// 执行数据库操作,最好通过数据库查询判断数据是否重复,以防万一。
DB operate...
} else {
// 业务到此为止,返回数据重复状态码
return "repeat data";
}
总结
方法有很多,这里只找了较为简单实用的,但缺点是仍然不能绝对的防止数据重复,至于原因尚未查明,之后有可能会补上更为复杂且精准的方案。