1.解决多人在线导出全量数据到Excel出现的cpu飚高问题
方案:
导出涉及到mysql查询的io操作,还涉及文件输入、输出流的io操作,所以对服务器的性能会影响的比较大
对导出操作进行排队,维护一个FIFO先进先出的队列,定队列一个固定size,在队列里面的人进行排队进行数据导出,导出完成后立马出队列,下一个排队的人进行操作;
还考虑到异步,可能还需要建个文件导出表,主要记录文件的导出情况,文件的存放地址,用户根据文件列表情况下载导出文件。
使用模板方式设计模式,实现导出的抽象类Export,封装easyexcel导出工具方法
大数量,进行分批,分sheet操作 ,子类实现抽象方法,查询总条数,查询数据list方法,模板方法父类中实现导出的具体实现,需要分多少批次,多少页,多少sheet页
都由导出的业务方决定,业务方继承模板实现对应方法即可
需要的表:公共异步任务表 记录导出任务id,执行状态,任务申请时间,导出文件url,文件总大小(count),
导出失败可以考虑有重试机制,重试的话重新排队,等待执行,同时更新异步任务表的状态,
通过这张公共异步任务表 可以维护一个下载中心,用户可以查询导出任务的执行情况,导出的文件
2.基于定时任务+大事务拆分方式实现多系统一致性补偿方案
对于分布式系统,没有任何人能保证远程调用不出问题,因此在做设计时,就必须能够对这种情况做出应对,把大事务拆分成小事务
拆分原则:
小事务内,尽量只有一个远程写操作
该远程写操作放到方法最后,保证在其返回成功后就能立刻提交事务
小事务可能会因为某些原因失败,因此需要机制来进行重试
方案:
a 新建一张事务任务表 任务类型,任务数据,错误信息,操作人,重试次数
作用:保存小事务的关键数据到data字段中,以保证通过该字段,就能正确执行小事务。另外也需要保存当前操作人的信息context。
b.通过定时任务,查出transaction_job表中未完成的数据,并执行对应的操作,这里通过简单的策略模式,将框架代码和业务代码做了分离
首先是框架代码核心逻辑
通过定时任务扫描任务,找到未完成的小事务
根据重试次数判断是否立刻执行,重试时间间隔随着重试次数的增多而逐渐增大
在分布式环境下需要加锁保证线程安全,
通过策略模式根据任务类型进行拆分,执行业务逻辑,成功则更新任务状态,失败记录失败原因,因为没有更新成功,后面定时任务还会继续重试
拆分完事务后,除第一个事务外,后续事务都是通过定时任务来执行的,
因此这些事务都存在一定的延迟,用户体验不好,解决办法也非常简单,只需要利用好Spring对于事务的生命周期管理
Spring提供的工具类 transactionSyncronizationManger.registerSyncronization 注册 afterCommit()方法,会在事务提交后执行 其他小时u我
可能存在添加任务后,定时任务也立刻扫描到了这条数据,同一任务就会被主线程与定时任务线程同时执行,
所以实际应用中需要考虑这个问题(比如:加锁再执行,执行前再检查数据库状态)
快速实现大文件下载的一种解决方案
1.请求
2.通过游标/流式
3.拿到迭代器iterator
4.循环小批量flush一批数据到客户端,知道查询无数据
生产实测结果
从546w存量数据中下载116w+数据(87MB)到csv文件,共计耗时不到30s,其中TTFB=9.25s
注意:
1.TTFB(收字节返回时间)时间必须小于网关的超时时间,否则会导致网关终止和后端的连接,导致后端出现broken pipe错误
2.在sql层面设置流查询最大超时时间,避免慢查询拖垮DB
3.只有使用mysql jdbc驱动的数据库,支持设置fetchSize=Integer.MIN_VALUE
4.mybatis-plus从3.4.2支持游标读,依赖mp后,排除所有mybatis和sqlparser依赖避免冲突
5.由于是长连接,所以游标必须在只读查询事务中使用,退出事务,游标自动关闭
这种方案的优点有:
1.更快的查询(游标\流式读)效率高于分页读
2.分批返回前端避免网关超时,也能让用户看到下载进度一直存在不至于直接关闭浏览器
3.无需上传oss后再下载,提高时效和节约磁盘资源
注意游标读/流式读是一种长连接,如果应用和DB之间存在NG等LB中间件,要注意超时(NG无法配置tcp连接保持,haproxy可以)!