如果单纯的按照业务逻辑先查询余额再扣除余额进行提现,请求少的时候不会有问题,一旦出现高并发或者用户恶意提现,那么就导致多次提现但是余额只扣了很少。这是由于高并发环境下(操作同一个用户的资金)一个线程进入读取余额100,还没更新完余额100-20,另一个请求就进入读取余额100(应该是80),问题就出现了。所以解决方案是限制一次只能一个线程操作。更为完善点,万一一瞬间有10000条对同一用户进行操作的请求到来,可以使用redis进行标记,加入有线程进入了,其它请求驳回。具体实现如下
Jedis jedis = getJedis();
//0不占用 1占用
if (jedis.get(userId + "") == null || jedis.get(userId + "").equals("0")) {
jedis.set(userId + "", "1");
} else if (jedis.get(userId + "").equals("1")) {
ServletUtilsEx.renderJson(response, new Result(Result.FAIL, "处理中,请稍后", null));
return;
} else {
ServletUtilsEx.renderJson(response, new Result(Result.FAIL, "failure", null));
return;
}
synchronized (this) {
User user = new User();
user.setId(userId);
user = userService.getUser(user);
if (user != null) {
//1.判断提现金额是否超过余额
//2.减去余额
}
}
jedis.set(userId + "", "0");
RedPackageUtils.returnResource(jedis);
//3.微信提现
这是在代码层面就行加锁,也可以在数据库层面进行控制。
select .. for update