如何将接口中的大事务进行优化

前言

作为后端开发的程序员,我们常常会的一些相对比较复杂的逻辑,比如我们需要给前端写一个调用的接口,这个接口需要进行相对比较复杂的业务逻辑操作,比如会进行,查询、远程接口或本地接口调用、更新、插入、计算等一些逻辑,将最终接口的返回结果给到前端,而经过这么一系列的业务逻辑操作,接口对DB的操作、对代码业务逻辑判断、进行接口调用这些都是需要时间的,而只要这是一个事务操作,每次对数据库进行的交互都会产生一条事务记录。

那么这样就会对我们接口返回的效率产生影响,而且这个影响是随着数据量的增长而增长的,这时候我们就需要对一整个大事务进行拆分,从而提升整体接口的效率。

何为大事务

就拿我最近开发写的一个接口来说吧,大致是这么一个逻辑,我需要根据页面的提交的数据生成一个收款单,整体接口处理的业务如下,我把它们写在了一个接口里,可以理解为这是一个大事物,这个接口执行的时间是相对比较长的,而且将这些逻辑全部写在一个接口里面,本身来说也是不太合理的。

大事务存在的一些问题

并发数据不一致

不加锁的情况下,由于种种原因第一次接口的调用还没执行完,还在等待第三方的调用回写数据,第二次调用又进来对数据进行了更改,第二次调用先执行完,这时候第一次接口调用拿到了第三方接口的返回,去回写状态发现已经被更新,导致无效操作。

加锁容易阻塞

加锁的情况下, 不会出现数据不一致情况,但是由于大事物执行时间较长,容易造成锁超时失效,锁定太多的数据造成阻塞,严重影响效率。

Undo logo事务日志性能问题

容易造成Undo logo日志数据量很大,降低了日志的查询性能,包括对事务的回滚效率也会降低。

并发数据库压力太大

并发量达到一定程度,会对数据库读写造成不小的压力,会堆积大量等待线程。

如何优化大事务

事务里面不要进行远程RPC调用

首先事务里面进行远程的接口调用,如果不采用分布式事务框架,本身就会存在事务不一致的情况,无法进行数据的回滚操作,并发情况下远程服务响应不及时,会出现接口返回不一致问题,当然必须采用异步调用,后面会提到。

编程型事务更加灵活

声明式事务只需要加在方法头加@Transactional注解即可开启事务,但是还是不太灵活,意味着整个方法所进行对数据库操作都要加进事务,当然一次查询也要进入事务,这并不是我们想要的,我们在update、insert操作上进行事务操作,方便进行回滚。

public Boolean transactionCommit(String userName) {
    //查询用户
    SysUser sysUser = userMapper.selectUserByUserName(userName,null);

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
            try {
                if (null != sysUser) {
                    //用户信息状态更新 status更新为1
                    userMapper.updateStatus(userName);
                }
            } catch (Exception e){
                //回滚
                transactionStatus.setRollbackOnly();
            }
        }
    });
    //再次查询
    SysUser sysUser1 = userMapper.selectUserByUserName(userName,"1");
    /log/.info("状态为1的用户信息"+JSON./toJSONString/(sysUser1));
    return  true;
}
复制代码

编程式事务的灵活点在于可以控制事务执行方法,运用transactionTemplate类进行事务操作,查询操作可以写在外面,这样查询获取数据的操作就不会进入mysql事务表。

数据分批处理

对于事务的更新或者插入,前端可能会有批量操作,大规模数据的批量更新、插入也会对事务接口产生影响,一旦其中有更新或插入失败,为了保证事务的一致性,整个操作都要进行回滚;

  • 前端:可以限制数据,对后端接口的访问,可以将数据进行分页,多次请求,可以避免事务提交大量数据。
  • 后端:也可以去数据进行分页处理,例如每次可以限制50条进行操作,如果是新增逻辑,使用Mybatis的批量更新大大提升效率,可以看看之前的文章juejin.cn/post/714455…
List<List<ReceivableFeeSaveDTO>> partition = Lists.partition(receivableFeeSaveDTOList, 50);
复制代码

大事务拆分小事务

可以将一个事务接口,拆分成多个事务接口,并且每个事务接口只做一件事,比如上面的收款单生成接口,金额回写、第三方接口调用、调用后的结果回写都可以抽成一个哥小事务接口。就好比做一件很复杂的事情,咋一眼看上去很复杂,但是我们把这复杂的步骤,进行多个步骤的拆分,每个阶段完成每个阶段的事情,就可以将整个过程简化,看起来就没那么复杂了。

异步并行处理

重中之重,事务里如果无法避免远程调用,那么肯定是需要进行异步调用,因为无法保证远程接口的及时响应性,CompletableFuture异步编排特性可以用到,task1和task2任务结束后,执行task3。

CompletableFuture<Object> task1 =CompletableFuture.supplyAsync(() -> {
    System.out.println("单号check线程" + Thread.currentThread().getId());
    //单号check接口 校验失败抛出异常

    return "账单实体信息";
}, executor);
CompletableFuture<Object> task2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("收款单生成线程" + Thread.currentThread().getId());
    try {
        //收款单生成

        return “账单编号”;
        Thread.sleep(3000);
        System.out.println("任务2结束:");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}, executor);

 //task1、task2 执行完执行task3 ,需要感知task1和task2的执行结果
CompletableFuture<Boolean> future = task1.thenCombineAsync(task2, (t1, t2) -> {

    System.out.println("账单金额回写线程" + Thread.currentThread().getId());
    // t1 、t2返回判断

    //回写返回结果
    return ture;
}, executor);

复制代码

总结

可见大事务是我们接口效率低下的罪魁祸首,有时候我们为了快速实现功能,可能会忽略一些关乎于性能的东西,而这些东西是我们能力提升的一个契机。

随着你的进步,你也许会有疑问之前为什么这么写代码,当你有这种感觉的时候,那么恭喜你,你已经站在另一个山岗,俯瞰山下一切都是那么的渺小,不多说我先去优化接口了~

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java优化编程(第2版)通过丰富、完整、富有代表性的实例,展示了如何提升Java应用性能,并且给出了优化前与优化后的Java应用程序的性能差别,以实际的实例与数字告诉你,为什么不可以这么做,应该怎么做,深入分析了影响Java应用程序性能的根本原因。本书不是教你怎样使用Java语言开发应用程序,而是教你怎样才能开发出更高效、更优秀的Java应用程序。书每一个例子都经过了作者严格的验证。 本书适合于所有想编写更高效、完美Java应用程序的开发人员阅读。 本书通过丰富、完整、富有代表性的实例,展示了如何提升Java应用性能,并且给出了优化前与优化后的Java应用程序的性能差别,以实际的实例与数字告诉你,为什么不可以这么做,应该怎么做,深入分析了影响Java应用程序性能的根本原因。本书不是教你怎样使用Java语言开发应用程序,而是教你怎样才能开发出更高效、更优秀的Java应用程序。书每一个例子都经过了作者严格的验证。<br> 本书适合于所有想编写更高效、完美Java应用程序的开发人员阅读.使用有道云笔记,轻松同步、管理您各终端的所有笔记。三重备份,存储数据安全有保障。免费的超大存储空间,无限量增。 目录: 第1章 java程序设计风格 1.1 java文件名与文件组织结构 1.2 java文件注释头 1.3 包的声明与引用 1.4 类与接口的声明 1.5 java源文件编排格式 代码行度与折行规则 1.6 程序注释 1.7 变量的声明初始化与放置 1.7.1 变量声明 1.7.2 变量初始化 1.7.3 变量放置 1.8 java程序语句编写规则 1.8.1 简单语句 1.8.2 复合语句 1.9 空格与空行的应用规则 1.9.1 空格的应用规则 1.9.2 空行的应用规则 1.10 方法、变量与常量的命名规则 1.10.1 方法的命名规则 . 1.10.2 变量的命名规则 1.10.3 常量的命名规则 1.11 java编程实践 1.11.1 访问实例与类变量的规则 1.11.2 引用类的静态变量与方法的 …… 小结 第4章 java核心类与性能优化 4.1 散列表类与性能优化 4.1.1 线程同步散列表类 4.1.2 设置arraylist初始化容量 4.1.3 arraylist与linkedlist 4.2 string类与性能优化 4.2.1 字符串累加与性能优化 4.2.2 字符串的length()方法与性能优化 4.2.3 tochararray()方法与性能优化 4.2.4 字符串转化为数字 4.3 系统i/o类 4.3.1 java语言输入/输出流 4.3.2 通过系统缓冲流类提高i/o操作效率 4.3.3 通过自定制缓冲区提高i/o操作效率 4.3.4 通过压缩流提高i/o操作效率 4.3.5 通过非阻塞i/o优化应用性能 4.4 其他 104 4.4.1 数据格式化与性能优化 4.4.2 获取文件信息与性能优化 小结 第5章 jni程序设计与性能优化 5.1 jni技术架构 5.2 创建带有本地方法的java应用 5.3 创建c端代码 5.3.1 创建c端代码头文件 5.3.2 创建c端代码主文件 5.4 jni技术数据类型与处理方法 5.4.1 jni技术的本地数据类型 5.4.2 访问jni本地数据类型的方法 5.4.3 在jni本地方法访问数组 5.4.4 jni的主要方法 5.5 jni的重要技术 5.5.1 局部引用与全局引用 5.5.2 处理本地方法引起的java错误 5.5.3 线程与本地方法 5.6 jni数学计算与性能优化 5.7 处理好jni文问题 小结 第6章 类与接口 6.1 类的构造器 6.1.1 构造器编写规则 6.2 类的继承规则 6.2.1 单线继承规则 6.2.2 包内部继承规则 6.2.3 逻辑包含继承规则 6.3 抽象类与接口 6.4 继承与组合的应用时机 6.5 接口与抽象类的应用时机 6.6 内部类 6.7 与性能相关的建议与经验 小结 第7章 jsp与servlet性能优化 7.1 提升jsp应用性能 7.1.1 优化jspinit()方法 7.1.2 通过优化_jspservice()方法提高系统性能 7.1.3 jsp高级知识 7.2 提升servlet应用性能 7.2.1 提高servlet应用性能的七个方法 7.2.2 合理缓冲静态数据与动态数据 7.2.3 改善servlet应用性能的方法 7.2.4 filter servlet与listener servlet 小结 第8章 开发高性能的ejb应用 8.1 采用ejb技术的必要性 8.1.1 ejb技术的优势特性 8.1.2 ejb技术体系具有清晰的架构层次 8.1.3 ejb与传统bean相比的性能优势 8.2 ejb的类型 8.2.1 ejb的生命周期 8.2.2 三种ejb的特点与适用场合 8.2.3 本地ejb与远程ejb的性能比较 8.2.4 有状态会话ejb与httpsession 8.2.5 ejb 3.0规范的ejb 8.3 优化无状态会话ejb性能 8.3.1 如何控制无状态会话ejb的生命周期 8.3.2 通过调节无状态会话ejb实例池的大小来优化系统性能 8.3.3 无状态会话ejb资源的缓冲与释放 8.4 优化有状态会话ejb性能 8.4.1 控制有状态会话ejb生命周期 8.4.2 优化有状态会话ejb的主要途径 8.5 优化实体ejb的性能 8.5.1 如何控制实体ejb的生命周期 8.5.2 通过调节实体ejb实例池的大小来优化系统性能 8.5.3 控制好实体ejb事务 8.5.4 提高实体ejb应用性能的其他知识 8.6 优化消息ejb性能 8.6.1 如何控制消息ejb的生命周期 8.6.2 如何缓存释放系统资源 8.7 几种ejb的结合应用规则 8.8 提高ejb应用性能的其他途径 小结 第9章 jms性能优化 9.1 jms消息收发模式及其各自适用场合 9.2 发送与接收jms消息 9.3 优化jms的会话对象 9.4 优化连接对象 9.5 优化消息目的地destination及消息生产者与消费者 9.6 优化消息对象及合理使用事务机制 9.7 影响jms性能的其他因素 小结 …… 第12章 java多线程技术与应用性能优化 12.1 java多线程技术 12.1.1 进程与线程 12.1.2 线程的生命周期 12.2 并行任务与性能 12.2.1 并行任务与多线程 12.2.2 并行任务与死锁 12.3 线程池技术与应用性能优化 12.3.1 线程池 12.3.2 调优线程池的尺寸 12.4 通过线程池技术优化套接字网络编程 小结 第13章 java泛型与应用优化 13.1 认识泛型 13.1.1 使用泛型的收益 13.1.2 泛型与jdk 5.0的集合类 13.2 使用泛型 13.2.1 创建支持泛型的类 13.2.2 泛型的自动解包装与自动包装的功能 13.2.4 限制泛型类型参数的范围 小结 第14章 ajax技术与web应用性能优化 14.1 了解ajax 14.2 通过ajax技术改善web应用性能 14.2.1 ajax技术实现 14.2.2 ajax技术性能优化实例 小结 第15章 其他优化话题 15.1 用weakhashmap屏蔽内存泄漏 15.2 优化java应用大小 15.3 通过randomaccess接口优化迭代列表 15.4 合并java的多进程与系统优化 小结 附录a together工具的使用简介 附录b j2se 5.0的新特性与性能的提升 附录c 编排代码的精美工具jxbeauty 使用有道云笔记,轻松同步、管理您各终端的所有笔记。三重备份,存储数据安全有保障。免费的超大存储空间,无限量增。激活后即可免费获得2GB云存储空间。赶紧来体验吧。 http://note.youdao.com/?invitation=6C359E8D3B4645CA9B2433C6E328E66F 使用有道云笔记,轻松同步、管理您各终端的所有笔记。三重备份,存储数据安全有保障。免费的超大存储空间,无限量增

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值