当前位置: 首页 > news >正文

网站注册价格2024年最新一轮阳性症状

网站注册价格,2024年最新一轮阳性症状,安居客网站怎么做,自己搭建服务器违法吗最近面试问到了银行转账的高并发问题,回答的不是很理想,小编整理了下,题目大概如下: 有一张银行账号表(银行账号字段、金额字段),A账号要给B账号转账,A扣款,B收款&#x…

最近面试问到了银行转账的高并发问题,回答的不是很理想,小编整理了下,题目大概如下:
有一张银行账号表(银行账号字段、金额字段),A账号要给B账号转账,A扣款,B收款,在多线程高并发情况下,A账户的金额不能小于0,问如何设计架构比较合理?
我一开始脑抽地回答了两个方案:
方案一:事务+同步锁/分布式锁(更新sql控制扣款update的账户金额要大于扣款金额)
方案二:将数据库缓存于redis,通过lua语句去执行查询判断扣款和收款,然后保证异步通知数据库更新

先来看第一个方案哈,同步锁在单节点的情况下确实可以解决问题,但是首先颗粒度大(不管哪个转账都得排队),且复杂度高的情况下就效率慢,其次若是多节点集群的情况下,同步锁就不适用,那我们看分布式锁(redis),分布式锁确实可以降低颗粒度,可以控制到A账户作为key锁,但是面试官提到了一个概念,redis脑裂(可能产生数据丢失),因此可能出现假锁的情况,因为面试的这家公司是做数字银行的,对于风险把控很严格,因此对于这类情况风险他们对这个方案也pass掉,不过我后面补充的这个扣款时sql需要增加当前账户金额需要大于扣款金额才能扣款,这个其实是可行的,这个后续代码会演示。

再来看第二个方案哈,这个缓存于redis的方案,其实我当时为什么会这么直接想到这个方案呢,首先是因为redis的单机命令操作,以及lua能保证多语句的执行,若账户金额不够扣款则不会进行转账,但是其实金额数据一般是不会缓存在redis中的,有一定的风险性且增加了系统复杂度,若数据库异常或其他情况导致的缓存数据不一致,金额这方面无法保证。

面试官还提到关于数据库隔离级别能否解决问题,其实我验证后关于可串行化其实也是只是对当前sql语句执行进行加锁,开启事务时可串行化也并非是对事务进行加锁,依然可能出现金额问题。(查询金额存在多个一样的情况)(但是其实可串行化在我之前了解的资料里理论上应该是可行的,有强制事务串行化,即按顺序提交

代码验证

锁机制在一定程度上可以,sql条件扣款时控制也是可以

@Service
@Slf4j
public class OperateAccountImpl implements OperateAccount {@Autowiredprivate AccountMapper accountMapper;@Autowiredprivate TransactionTemplate transactionTemplate;/*** 处理账户转账方案总结:* 方案一:事务+同步锁(颗粒度大,逻辑复杂效率慢,只适用单机)/分布式锁(可能出现脑裂假锁的情况)* 方案二:事务+sql条件控制(账户金额需大于等于扣款金额,但是查询时可能出现一次可扣款数据)*/@Override
//    @Transactional(rollbackFor= Exception.class)public String transfer(String accountFrom, String accountTo, double amount) {// 设置隔离级别为串行化(这里测试出来会死锁,按理解串行化应该是事务串行化,不应该抢锁才对)//transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);//线程标记String threadName = "【"+Thread.currentThread().getName()+"】";String msg = "";List<String> msgList = new ArrayList<>();//同步锁也是实现方法之一
//        synchronized(this){
//
//        }try{//开启Spring事务transactionTemplate.execute(new TransactionCallbackWithoutResult() {protected void doInTransactionWithoutResult(TransactionStatus status) {try {QueryWrapper<AccountDto> queryWrapper =new QueryWrapper<>();queryWrapper.select("account","money").eq("account",accountFrom);// 查询金额是否够扣AccountDto accountDto = accountMapper.selectOne(queryWrapper);
//                        AccountDto accountDto = accountMapper.selectByForUpdate(accountFrom);if(null == accountDto){throw new Exception("账户"+accountFrom+"不存在");}log.info("{} 查询到扣款账户{} 余额:{}",threadName,accountFrom,accountDto.getMoney());if (accountDto.getMoney() < amount) {throw new Exception("余额不足,账户"+accountFrom+"只剩:"+accountDto.getMoney());}// 扣款int count1 = accountMapper.update(null,transferUpdate(-1 * amount,accountFrom));if (count1 == 0) {throw new Exception("账户"+accountFrom+"扣款失败,检查余额");}// 收款int count2 = accountMapper.update(null,transferUpdate(amount,accountTo));if (count2 == 0) {throw new Exception("账户"+accountTo+"收款失败");}log.info(threadName+"转账成功");msgList.add(threadName+"转账成功");}catch (Exception e){log.info(threadName+e.getMessage());msgList.add(threadName+e.getMessage());// 回滚status.setRollbackOnly();}}});}catch (Exception e){
//            msgList.add(e.getMessage());}//        log.info(threadName+"结束:"+msgList.toString());return msgList.get(0);}// 更新sql操作public UpdateWrapper<AccountDto> transferUpdate(double updateMoney,String account){UpdateWrapper<AccountDto> updateWrapper = Wrappers.update();// 修改表中money字段为指定的数据
//        updateWrapper.set("money", updateMoney);updateWrapper.setSql("money = money + "+ updateMoney);if (updateMoney<0){// 修改条件为account=?且大于等于扣款金额的数据updateWrapper.eq("account", account).and(wq ->wq.ge("money",-1 * updateMoney));}else{// 修改条件为account=?的数据updateWrapper.eq("account", account);}//        //若使用事务隔离级别为最高级,测试出来的结果加锁的是sql并不是事务,因此查询值依然没有顺序之分,分开sql依旧会出现问题(实际上不会扣款扣多,但是会死锁,因为会抢占sql的锁)
//        updateWrapper.eq("account", account);return updateWrapper;}
}

通过多线程并发执行测试

测试结论:
方案一:事务+同步锁(颗粒度大,逻辑复杂效率慢,只适用单机)/分布式锁(可能出现脑裂假锁的情况)
方案二:事务+sql条件控制(账户金额需大于等于扣款金额,但是查询时可能出现一次可扣款数据)

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate OperateAccount operateAccount;//设置固定线程池ExecutorService executorService = Executors.newFixedThreadPool(10);@RequestMapping("/transfer")public String transfer(HttpServletRequest request){//创建同步计数器(10个一起跑)CountDownLatch countDownLatch = new CountDownLatch(10);//用于堵塞线程等待全部结果CountDownLatch countDownLatch1 = new CountDownLatch(10);//扣款账户String accountFrom = request.getParameter("accountFrom");//收款账户String accountTo = request.getParameter("accountTo");//转账金额double amount = Double.parseDouble(request.getParameter("amount"));List<String> msgList = new ArrayList<>();try {// 模拟转账操作for (int i = 0; i < 10; i++) {executorService.submit(() -> {try {countDownLatch.await();//统一等待} catch (InterruptedException e) {e.printStackTrace();}String msg = operateAccount.transfer(accountFrom, accountTo, amount);msgList.add(msg);countDownLatch1.countDown();});//处理完同步计数器线程数减一,待计数器为0统一执行所有转账操作countDownLatch.countDown();}countDownLatch1.await();executorService.shutdown();}catch (Exception e){e.printStackTrace();}//        log.info("转账结果:{}",msgList.toString());return msgList.toString();}}

小编比较菜…欢迎评论区讨论更好的方法❀❀❀

http://www.yayakq.cn/news/566477/

相关文章:

  • 怎么做html5网站营销型网站建设多少钱
  • 东莞高埗网站建设网站面向哪些地区做优化容易
  • 个人网站建设价格wordpress自建站上可以买卖
  • 免费推广网站途径有哪些引流推广推广微信hyhyk1效果好
  • 网站购买空间多少钱制作一个网站平台要多钱
  • 服饰类网站模板东吴网架公司
  • 旅游电子商务与网站建设北京网站备案注销中心
  • 电子商务怎么做网站我有多个单页网站需要备案吗
  • seo 网站 制作网站美工工作流程
  • 所有爱做网站潍坊做网站软件
  • 手机网站开发程序员自己做的网站什么时候可以赚钱
  • 帮别人做违法网站会怎么样源码下载工具
  • 邯郸网站建设怎么做浙江建设厅网站 打不开
  • 长沙岳麓区百度seo关键词优化市场
  • 做地暖工程的网站怎么做视频解析网站吗
  • 德语网站建设注意事项学校 网站建设招聘
  • 好看的网站源码网站开发需要的技术人员有什么软件
  • 网站域名space住房建设厅官网
  • 深圳模板网站建设哪家好建设银行网站修改
  • 注册网站请签署意见是写无山西旅游网站建设
  • 营销型网站建设哪里有海淀企业型网站建设
  • 大型购物网站有哪些有哪些做特卖的网站有哪些
  • 无忧自助建站html门户网站模板
  • wordpress后台不能登陆优化网站多少钱
  • 有什么网站可以做试题菏泽做网站的工作室
  • asp.net做网站步骤公司网站如何制作设计
  • 高校网站建设汇报夸克观看免费视频
  • 鞍山建设集团网站外国贸易平台
  • 如何发布网站到域名中国中国建设银行网站首页
  • 企业做网站的流程德州购物网站建设