0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

支付宝:多线程事务怎么回滚?

jf_ro2CN3Fa 来源:CSDN 2023-01-09 11:42 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群


背景介绍

1,最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆分数据并行处理来提高响应时间,如果有一个线程执行失败,则全部回滚。

2,在spring中可以使用@Transactional注解去控制事务,使出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚,导致数据错误。

3,下面用一个简单示例演示多线程事务。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

公用的类和方法

/**
*平均拆分list方法.
*@paramsource
*@paramn
*@param
*@return
*/
publicstaticList>averageAssign(Listsource,intn){
List>result=newArrayList>();
intremaider=source.size()%n;
intnumber=source.size()/n;
intoffset=0;//偏移量
for(inti=0;ivalue=null;
if(remaider>0){
value=source.subList(i*number+offset,(i+1)*number+offset+1);
remaider--;
offset++;
}else{
value=source.subList(i*number+offset,(i+1)*number+offset);
}
result.add(value);
}
returnresult;
}
/**线程池配置
*@versionV1.0
*/
publicclassExecutorConfig{
privatestaticintmaxPoolSize=Runtime.getRuntime().availableProcessors();
privatevolatilestaticExecutorServiceexecutorService;
publicstaticExecutorServicegetThreadPool(){
if(executorService==null){
synchronized(ExecutorConfig.class){
if(executorService==null){
executorService=newThreadPool();
}
}
}
returnexecutorService;
}

privatestaticExecutorServicenewThreadPool(){
intqueueSize=500;
intcorePool=Math.min(5,maxPoolSize);
returnnewThreadPoolExecutor(corePool,maxPoolSize,10000L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<>(queueSize),newThreadPoolExecutor.AbortPolicy());
}
privateExecutorConfig(){}
}
/**获取sqlSession
*@author86182
*@versionV1.0
*/
@Component
publicclassSqlContext{
@Resource
privateSqlSessionTemplatesqlSessionTemplate;

publicSqlSessiongetSqlSession(){
SqlSessionFactorysqlSessionFactory=sqlSessionTemplate.getSqlSessionFactory();
returnsqlSessionFactory.openSession();
}
}

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

示例事务不成功操作

/**
*测试多线程事务.
*@paramemployeeDOList
*/
@Override
@Transactional
publicvoidsaveThread(ListemployeeDOList){
try{
//先做删除操作,如果子线程出现异常,此操作不会回滚
this.getBaseMapper().delete(null);
//获取线程池
ExecutorServiceservice=ExecutorConfig.getThreadPool();
//拆分数据,拆分5份
List>lists=averageAssign(employeeDOList,5);
//执行的线程
Thread[]threadArray=newThread[lists.size()];
//监控子线程执行完毕,再执行主线程,要不然会导致主线程关闭,子线程也会随着关闭
CountDownLatchcountDownLatch=newCountDownLatch(lists.size());
AtomicBooleanatomicBoolean=newAtomicBoolean(true);
for(inti=0;iif(i==lists.size()-1){
atomicBoolean.set(false);
}
Listlist=lists.get(i);
threadArray[i]=newThread(()->{
try{
//最后一个线程抛出异常
if(!atomicBoolean.get()){
thrownewServiceException("001","出现异常");
}
//批量添加,mybatisPlus中自带的batch方法
this.saveBatch(list);
}finally{
countDownLatch.countDown();
}

});
}
for(inti=0;i//当子线程执行完毕时,主线程再往下执行
countDownLatch.await();
System.out.println("添加完毕");
}catch(Exceptione){
log.info("error",e);
thrownewServiceException("002","出现异常");
}finally{
connection.close();
}
}

数据库中存在一条数据:

07f6f516-8fc2-11ed-bfe3-dac502259ad0.png
//测试用例
@RunWith(SpringRunner.class)
@SpringBootTest(classes={ThreadTest01.class,MainApplication.class})
publicclassThreadTest01{

@Resource
privateEmployeeBOemployeeBO;

/**
*测试多线程事务.
*@throwsInterruptedException
*/
@Test
publicvoidMoreThreadTest2()throwsInterruptedException{
intsize=10;
ListemployeeDOList=newArrayList<>(size);
for(inti=0;inewEmployeeDO();
employeeDO.setEmployeeName("lol"+i);
employeeDO.setAge(18);
employeeDO.setGender(1);
employeeDO.setIdNumber(i+"XX");
employeeDO.setCreatTime(Calendar.getInstance().getTime());
employeeDOList.add(employeeDO);
}
try{
employeeBO.saveThread(employeeDOList);
System.out.println("添加成功");
}catch(Exceptione){
e.printStackTrace();
}
}
}

测试结果:

08028656-8fc2-11ed-bfe3-dac502259ad0.png08120248-8fc2-11ed-bfe3-dac502259ad0.png

可以发现子线程组执行时,有一个线程执行失败,其他线程也会抛出异常,但是主线程中执行的删除操作,没有回滚,@Transactional注解没有生效。

使用sqlSession控制手动提交事务

@Resource
SqlContextsqlContext;
/**
*测试多线程事务.
*@paramemployeeDOList
*/
@Override
publicvoidsaveThread(ListemployeeDOList)throwsSQLException{
//获取数据库连接,获取会话(内部自有事务)
SqlSessionsqlSession=sqlContext.getSqlSession();
Connectionconnection=sqlSession.getConnection();
try{
//设置手动提交
connection.setAutoCommit(false);
//获取mapper
EmployeeMapperemployeeMapper=sqlSession.getMapper(EmployeeMapper.class);
//先做删除操作
employeeMapper.delete(null);
//获取执行器
ExecutorServiceservice=ExecutorConfig.getThreadPool();
List>callableList=newArrayList<>();
//拆分list
List>lists=averageAssign(employeeDOList,5);
AtomicBooleanatomicBoolean=newAtomicBoolean(true);
for(inti=0;iif(i==lists.size()-1){
atomicBoolean.set(false);
}
Listlist=lists.get(i);
//使用返回结果的callable去执行,
Callablecallable=()->{
//让最后一个线程抛出异常
if(!atomicBoolean.get()){
thrownewServiceException("001","出现异常");
}
returnemployeeMapper.saveBatch(list);
};
callableList.add(callable);
}
//执行子线程
List>futures=service.invokeAll(callableList);
for(Futurefuture:futures){
//如果有一个执行不成功,则全部回滚
if(future.get()<=0){
connection.rollback();
return;
}
}
connection.commit();
System.out.println("添加完毕");
}catch(Exceptione){
connection.rollback();
log.info("error",e);
thrownewServiceException("002","出现异常");
}finally{
connection.close();
}
}
//sql
"saveBatch"parameterType="List">
INSERTINTO
employee(employee_id,age,employee_name,birth_date,gender,id_number,creat_time,update_time,status)
values
="list"item="item"index="index"separator=",">
(
#{item.employeeId},
#{item.age},
#{item.employeeName},
#{item.birthDate},
#{item.gender},
#{item.idNumber},
#{item.creatTime},
#{item.updateTime},
#{item.status}
)


数据库中一条数据:

081f036c-8fc2-11ed-bfe3-dac502259ad0.png

测试结果:抛出异常,

0836bc00-8fc2-11ed-bfe3-dac502259ad0.png

删除操作的数据回滚了,数据库中的数据依旧存在,说明事务成功了。

0847e7d2-8fc2-11ed-bfe3-dac502259ad0.png

成功操作示例:

@Resource
SqlContextsqlContext;
/**
*测试多线程事务.
*@paramemployeeDOList
*/
@Override
publicvoidsaveThread(ListemployeeDOList)throwsSQLException{
//获取数据库连接,获取会话(内部自有事务)
SqlSessionsqlSession=sqlContext.getSqlSession();
Connectionconnection=sqlSession.getConnection();
try{
//设置手动提交
connection.setAutoCommit(false);
EmployeeMapperemployeeMapper=sqlSession.getMapper(EmployeeMapper.class);
//先做删除操作
employeeMapper.delete(null);
ExecutorServiceservice=ExecutorConfig.getThreadPool();
List>callableList=newArrayList<>();
List>lists=averageAssign(employeeDOList,5);
for(inti=0;ilist=lists.get(i);
Callablecallable=()->employeeMapper.saveBatch(list);
callableList.add(callable);
}
//执行子线程
List>futures=service.invokeAll(callableList);
for(Futurefuture:futures){
if(future.get()<=0){
connection.rollback();
return;
}
}
connection.commit();
System.out.println("添加完毕");
}catch(Exceptione){
connection.rollback();
log.info("error",e);
thrownewServiceException("002","出现异常");
//thrownewServiceException(ExceptionCodeEnum.EMPLOYEE_SAVE_OR_UPDATE_ERROR);
}
}

测试结果:

08585db0-8fc2-11ed-bfe3-dac502259ad0.png

数据库中数据:

删除的删除了,添加的添加成功了,测试成功。

0869dc84-8fc2-11ed-bfe3-dac502259ad0.png

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 多线程
    +关注

    关注

    0

    文章

    279

    浏览量

    21150
  • spring
    +关注

    关注

    0

    文章

    345

    浏览量

    16096

原文标题:支付宝:多线程事务怎么回滚?说用 @Transactional 可以回去等通知了!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【瑞萨RA × Zephyr评测】多线程和看门狗

    本文章旨在评估使用 Zephyr RTOS 在 Renesas FPB-RA6E2 开发板上实现多线程调度与硬件看门狗功能的应用。评估内容包括任务调度、看门狗初始化流程、主程序逻辑的详细解析,以及实验现象与数据分析。
    的头像 发表于 01-10 10:23 2783次阅读
    【瑞萨RA × Zephyr评测】<b class='flag-5'>多线程</b>和看门狗

    支付宝FluidMarkdown鸿蒙版正式开源

    近日,支付宝正式开源面向鸿蒙平台的 FluidMarkdown ——一款专为智能化业务场景打造的移动端 Markdown 渲染库。作为首个适配 HarmonyOS 平台的流式 Markdown 引擎
    的头像 发表于 12-26 13:42 917次阅读

    华为与中移互联网、支付宝签署战略合作协议

    今日,为推进AI新通信生态繁荣,中移互联网有限公司(以下简称“中移互联网”)、支付宝(杭州)数字服务技术有限公司(以下简称“支付宝”)及华为技术有限公司(以下简称“华为”)正式签署合作协议,共同开启
    的头像 发表于 12-17 16:41 914次阅读

    多线程的系统

    多线程系统的事件响应也是在中断中完成的,但事件的处理是在线程中完成的。在多线程系统中,线程跟中断一样,也具有优先级,优先级高的线程会被优先执
    发表于 12-08 07:55

    Linux多线程对比单线程的优势

    :「资源利用率」:通过多线程,可以更有效地利用CPU资源,特别是多核CPU。「并行处理」:线程允许同时执行多个任务,提高程序的执行效率。「简化设计」:使用线程可以简化程序设计,因为线程
    发表于 12-01 06:11

    支付宝“碰一下”的革新背后:国民技术MCU的隐形力量

    近日,全球顶尖金融科技盛会Money20/20公布首届创新大奖TheMoneyAwards结果,“支付宝碰一下”从众多参赛企业中脱颖而出,凭借创新的解决方案和极致的用户体验摘得“支付”类别大奖,成为
    的头像 发表于 11-21 19:15 1564次阅读
    <b class='flag-5'>支付宝</b>“碰一下”的革新背后:国民技术MCU的隐形力量

    基于IAP功能实现远程升级,如何设计Flash双Bank热切换的机制?

    基于IAP功能实现远程升级时,如何设计Flash双Bank热切换的机制?
    发表于 11-21 07:26

    广汽能源与支付宝深化战略合作

    2025年11月14日,广汽能源科技有限公司(以下简称“广汽能源”)与支付宝(杭州)数字服务技术有限公司(以下简称“支付宝”)在广汽集团总部举行深化合作签约仪式。广汽集团副总经理郑衡、蚂蚁集团支付宝
    的头像 发表于 11-18 10:19 872次阅读

    国民技术MCU芯片护航支付宝碰一下设备创新

    近日,全球顶尖金融科技盛会Money20/20公布首届创新大奖The Money Awards结果,“支付宝碰一下”从众多参赛企业中脱颖而出,凭借创新的解决方案和极致的用户体验摘得“支付”类别大奖,成为该类别中唯一的中国企业。
    的头像 发表于 11-06 10:15 1396次阅读

    rt-thread studio 如何进行多线程编译?

    ,使用的是5800h+32g内存+sn550 ssd,开启16线程编译时cpu的占用率也只能到30%,编译完整个工程需要3分钟 感觉多线程编译设置没有生效,有办法提高编译速度吗 rtthread studio版本是 2.2.9
    发表于 10-11 09:16

    淘宝/天猫:使用支付宝API实现多场景支付,覆盖用户偏好

    ​  在淘宝和天猫等电商平台上,支付体验直接影响用户满意度和转化率。支付宝作为核心支付工具,其开放API(Application Programming Interface)允许开发者灵活集成多场景
    的头像 发表于 09-25 09:59 1076次阅读
    淘宝/天猫:使用<b class='flag-5'>支付宝</b>API实现多场景<b class='flag-5'>支付</b>,覆盖用户偏好

    【HZ-T536开发板免费体验】—— linux创建线程

    自己的私有资源。 在linux系统中,线程状态通常反映了当前线程的当前活动和执行阶段。 主要分为: 1。运行转态 2。阻塞转态 3。终止状态 如何区分单线程多线程? 在单个程序中只
    发表于 09-01 21:31

    奥比中光助力支付宝碰一下落地电梯场景

    近日,支付宝与分众传媒宣布联合推出“碰一下抢红包”服务。作为创新交互方式,“支付宝碰一下”首次被引入至电梯场景,并已在全国20余个城市的电梯铺设。奥比中光作为“支付宝碰一下”业务的核心供应商,为这一创新交互方式首次大规模落地电梯
    的头像 发表于 08-12 11:32 1503次阅读

    多线程的安全注意事项

    多线程安全是指多个线程同时访问或修改共享资源时,能够保证程序的正确性和可靠性。 开发者选择TaskPool或Worker进行多线程开发时,在TaskPool和Worker的工作线程中导
    发表于 06-20 07:49

    碰一下终端,让自助售货机秒变 “家里的冰箱”

    继刷脸支付后,支付宝近日又推出了新的支付方式——碰一下支付。只需将手机轻轻靠近支付宝“碰一下”支付
    的头像 发表于 06-18 10:49 2275次阅读
    碰一下终端,让自助售货机秒变 “家里的冰箱”