支付宝一面多线程事务怎么回滚?
背景介绍
1.最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆分数据并行处理来提高响应时间,如果有一个线程执行失败,则全部回滚。
2.在spring中可以使用@Transactional注解去控制事务,使出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚,导致数据错误。
3.下面用一个简单示例演示多线程事务。 公用的类和方法/** * 平均拆分list方法. * @param source * @param n * @param * @return */ public static List> averageAssign(List source,int n){ List> result=new ArrayList>(); int remaider=source.size()%n; int number=source.size()/n; int offset=0;//偏移量 for(int i=0;i value=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); } return result; } /** 线程池配置 * @version V1.0 */ public class ExecutorConfig { private static int maxPoolSize = Runtime.getRuntime().availableProcessors(); private volatile static ExecutorService executorService; public static ExecutorService getThreadPool() { if (executorService == null){ synchronized (ExecutorConfig.class){ if (executorService == null){ executorService = newThreadPool(); } } } return executorService; } private static ExecutorService newThreadPool(){ int queueSize = 500; int corePool = Math.min(5, maxPoolSize); return new ThreadPoolExecutor(corePool, maxPoolSize, 10000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(queueSize),new ThreadPoolExecutor.AbortPolicy()); } private ExecutorConfig(){} } /** 获取sqlSession * @author 86182 * @version V1.0 */ @Component public class SqlContext { @Resource private SqlSessionTemplate sqlSessionTemplate; public SqlSession getSqlSession(){ SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory(); return sqlSessionFactory.openSession(); } } 示例事务不成功操作/** * 测试多线程事务. * @param employeeDOList */ @Override @Transactional public void saveThread(List employeeDOList) { try { //先做删除操作,如果子线程出现异常,此操作不会回滚 this.getBaseMapper().delete(null); //获取线程池 ExecutorService service = ExecutorConfig.getThreadPool(); //拆分数据,拆分5份 List> lists=averageAssign(employeeDOList, 5); //执行的线程 Thread []threadArray = new Thread[lists.size()]; //监控子线程执行完毕,再执行主线程,要不然会导致主线程关闭,子线程也会随着关闭 CountDownLatch countDownLatch = new CountDownLatch(lists.size()); AtomicBoolean atomicBoolean = new AtomicBoolean(true); for (int i =0;i list = lists.get(i); threadArray[i] = new Thread(() -> { try { //最后一个线程抛出异常 if (!atomicBoolean.get()){ throw new ServiceException("001","出现异常"); } //批量添加,mybatisPlus中自带的batch方法 this.saveBatch(list); }finally { countDownLatch.countDown(); } }); } for (int i = 0; i employeeDOList = new ArrayList<>(size); for (int i = 0; i employeeDOList) throws SQLException { // 获取数据库连接,获取会话(内部自有事务) SqlSession sqlSession = sqlContext.getSqlSession(); Connection connection = sqlSession.getConnection(); try { // 设置手动提交 connection.setAutoCommit(false); //获取mapper EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class); //先做删除操作 employeeMapper.delete(null); //获取执行器 ExecutorService service = ExecutorConfig.getThreadPool(); List> callableList = new ArrayList<>(); //拆分list List> lists=averageAssign(employeeDOList, 5); AtomicBoolean atomicBoolean = new AtomicBoolean(true); for (int i =0;i list = lists.get(i); //使用返回结果的callable去执行, Callable callable = () -> { //让最后一个线程抛出异常 if (!atomicBoolean.get()){ throw new ServiceException("001","出现异常"); } return employeeMapper.saveBatch(list); }; callableList.add(callable); } //执行子线程 List> futures = service.invokeAll(callableList); for (Future future:futures) { //如果有一个执行不成功,则全部回滚 if (future.get()<=0){ connection.rollback(); return; } } connection.commit(); System.out.println("添加完毕"); }catch (Exception e){ connection.rollback(); log.info("error",e); throw new ServiceException("002","出现异常"); }finally { connection.close(); } } // sql INSERT INTO employee (employee_id,age,employee_name,birth_date,gender,id_number,creat_time,update_time,status) values ( #{item.employeeId}, #{item.age}, #{item.employeeName}, #{item.birthDate}, #{item.gender}, #{item.idNumber}, #{item.creatTime}, #{item.updateTime}, #{item.status} )
数据库中一条数据:
测试结果,抛出异常:
删除操作的数据回滚了,数据库中的数据依旧存在,说明事务成功了。
成功操作示例: @Resource SqlContext sqlContext; /** * 测试多线程事务. * @param employeeDOList */ @Override public void saveThread(List employeeDOList) throws SQLException { // 获取数据库连接,获取会话(内部自有事务) SqlSession sqlSession = sqlContext.getSqlSession(); Connection connection = sqlSession.getConnection(); try { // 设置手动提交 connection.setAutoCommit(false); EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class); //先做删除操作 employeeMapper.delete(null); ExecutorService service = ExecutorConfig.getThreadPool(); List> callableList = new ArrayList<>(); List> lists=averageAssign(employeeDOList, 5); for (int i =0;i list = lists.get(i); Callable callable = () -> employeeMapper.saveBatch(list); callableList.add(callable); } //执行子线程 List> futures = service.invokeAll(callableList); for (Future future:futures) { if (future.get()<=0){ connection.rollback(); return; } } connection.commit(); System.out.println("添加完毕"); }catch (Exception e){ connection.rollback(); log.info("error",e); throw new ServiceException("002","出现异常"); // throw new ServiceException(ExceptionCodeEnum.EMPLOYEE_SAVE_OR_UPDATE_ERROR); } }
测试结果:
数据库中数据:
删除的删除了,添加的添加成功了,测试成功。
来源:blog.csdn.net/weixin_43225491/article/details/117705686
RedmiK50宇宙来袭RedmiK40系列是2021的一匹黑马。它在618年赢得了销售冠军,半年多来价格几乎没有波动。可以看出这个模型有多香。(主要是12256g版本)虽然K40的价格相对较低,还有使用
骑行井陉九莲洞挂云山骑行井陉九莲洞挂云山爬山农历初三,天气晴好,零下6度,微风23级,适合骑行和爬山。计划骑行到九莲洞,看看溶洞。然后再去挂云山,全程100公里,力度适中。挂云山风景骑行组7点出发,十
最便宜的34英寸120Hz4K电竞带鱼屏亲测靠谱放心买21934英寸高刷新率带鱼屏,这四个关键字放在一起,不少人的第一反应是这得很贵吧?放在几年以前,这几个参数放一起显然便宜不到哪去,而现在,随着面板成本降低,带鱼屏的价格也逐渐降了下
共享冬奥盛会照金滑雪场好火热随着北京冬奥会的正式开启,冰雪运动热情彻底被点燃。2月5日,照金滑雪场在严格落实疫情防控各项措施的前提下,迎来了今冬以来最大的客流量,单日入场游客数量突破5000人大关。来自西安的
献礼冬奥八位小歌手唱响冰雪相约2022北京冬奥会即将拉开帷幕。近日,由范坤作曲,王砚玺丁海涛作词,邵爱然崔雅瑄贾子峻刘奕焓张艺潇郭恩泽张爱卿秦梓迅八位小歌手演唱的歌曲冰雪相约发布MV,为冬奥献礼。2022冬奥会
冬奥会复兴号5G智能列车震惊外国媒体,感叹中国科技至少领先10年2022年北京冬奥会,外国记者来到中国,第一时间就被中国的一系列科技所震撼。公寓入住移动售货社区配送点菜送餐客房服务导览翻译安防巡检颁奖器械运送等九大场景,无不彰显中国科技崛起迅速
京西小三峡丨不出京感受低配版雅鲁藏布江大拐弯丨附详细攻略新年登高,这一次选择的是位于北京市门头沟珠窝村的京西小三峡。导航至珠窝村,上山的路不需要进村,但是需要在村口门卫处扫健康宝。上山路就是三岔路口的中间岔路,一路向北,沿途路左侧有一个
美国媒体爆料,法拉第可能卖不出一辆车,贾跃亭被认定是骗子?2017年,乐视系崩盘。贾跃亭于当年7月份远赴美国造车,至今未归!说句实话,这几年但凡是贾跃亭的新闻,基本上都是负面的。为何呢?因为在大部分人眼中,贾跃亭俨然成为了一个骗子。都说阿
一爬到底的宝宝更聪明?错过爬行期难补救,要学会引导文宋妈说育儿(原创文章,欢迎转载分享)老话说三翻六坐,七牙八爬,从字面意思不难看出,宝宝大约八九个月大时,就会爬了。当然,并非所有宝宝都按照固定套路出牌,一些宝宝一岁多还不会爬,甚
养二胎比养一胎要多花钱?宝妈晒年度养娃账单,结果和你想的不同普遍想法是,养两个孩子的花销要比养一个多很多。一提到二胎,很多父母想到的第一个制约条件是穷。觉得以自己现在的经济条件,很难供给两个孩子的花销。毕竟孩子的吃穿用度都要花钱,不管上不上
怀孕期间如何保胎?做好这几点,保证胎儿健康平安十月怀胎,一朝分娩,怀孕期间虽然辛苦,但很快乐,能跟胎儿共同享受心跳和血液。对孕妇来说,胎儿健康是最为重要的,整个怀孕期应做到以下几点来保证胎儿健康平安。怀孕期间该如何保胎?1多注