闲谭SpringBoot--ShardingSphere分布式事务探究

发布于:2025-02-10 ⋅ 阅读:(51) ⋅ 点赞:(0)

0. 背景

接上篇文章《闲谭SpringBoot–ShardingSphere分库分表探究》,我已经完成分库分表的项目搭建。

本篇探究在不同情况下,事务是否正常运作。

1. 未分库分表时

针对不分库的user表,故意抛出异常,看看能否回滚。

执行前:
在这里插入图片描述
执行代码:

     /**
     * 在未分库分表的情况下,可以正常回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public int testWithNoDivide() throws Exception {
        UserDo user = new UserDo();
        user.setId("2");
        user.setName("testWithNoDivide");
        int re = userService.add(user);
        if (true) {
            throw new Exception("err");
        }
        return re;
    }

执行后,由于抛出异常,事务回滚,数据库中的内容并未改变。

测试通过。

2. 仅分表时

如果将log表按时间进行分表,我们看能否回滚。

首先验证只使用@Transactional(rollbackFor = Exception.class)能否回滚多个表:

	/**
     * 在仅分表的情况下,测试是否可以回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public int testWithTableDivide() throws Exception {
        // 改用户
        UserDo user = new UserDo();
        user.setId("2");
        user.setName("testWithTableDivide");
        int reUser = userService.add(user);
        // 改日志
        LogDo log = new LogDo();
        log.setId("111");
        log.setTime("2025-01-01 11:11:11");
        log.setContent("testWithTableDivide");
        int reLog = logService.add(log);
        // 抛出异常
        if (true) {
            throw new Exception("err");
        }
        return reUser + reLog;
    }

通过验证,发现是可以正常回滚的,所以在仅分表不分库的情况下,使用@Transactional(rollbackFor = Exception.class)足矣。

3. 分库分表时

如果既分库、又分表,是否需要开启分布式事务呢?

我们分两步验证,首先验证不涉及分库的表时,其次验证涉及分库的表时。

3.1 不涉及分库表

此时对log分库分表,测试user是否能正常回滚:

	/**
     * 在未分库分表的情况下,可以正常回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public int testWithNoDivide() throws Exception {
        UserDo user = new UserDo();
        user.setId("2");
        user.setName("testWithNoDivide");
        int re = userService.add(user);
        if (true) {
            throw new Exception("err");
        }
        return re;
    }

这个应该是没有悬念的,不涉及分库的表,没必要开启分布式事务。

3.2 涉及分库表,且分库表处于一个库

我们同时修改user表、log表,但是让user和log处于一个数据库ds0里面,测试能否回滚

  	/**
     * 涉及分库表,且分库表处于一个库
     */
    @Transactional(rollbackFor = Exception.class)
    public int testDbDivdeButInOneDb() throws Exception {
        // 改用户
        UserDo user = new UserDo();
        user.setId("2");
        user.setName("testDbDivdeButInOneDb");
        int reUser = userService.add(user);
        // 改日志1
        LogDo log1 = new LogDo();
        log1.setId("111");
        log1.setTime("2025-01-01 11:11:11");
        log1.setContent("testDbDivdeButInOneDb");
        log1.setDepartId("0");
        int reLog1 = logService.add(log1);
        // 改日志2
        LogDo log2 = new LogDo();
        log2.setId("222");
        log2.setTime("2025-02-02 11:11:11");
        log2.setContent("testDbDivdeButInOneDb");
        log2.setDepartId("0");
        int reLog2 = logService.add(log2);
        // 抛出异常
        if (true) {
            throw new Exception("err");
        }
        return reUser + reLog1 + reLog2;
    }

正常回滚,说明当表在一个库中时,使用@Transactional(rollbackFor = Exception.class)即可。

3.3 涉及分库表,且分库表处于多个库

我们再构造一个在不同库的例子,看能否回滚。

	/**
     * 涉及分库表,且分库表处于多个库
     */
    @Transactional(rollbackFor = Exception.class)
    public int testDbDivdeButInTwoDb() throws Exception {
        // 改日志1
        LogDo log1 = new LogDo();
        log1.setId("111");
        log1.setTime("2025-01-01 11:11:11");
        log1.setContent("testDbDivdeButInOneDb");
        log1.setDepartId("0");//在库ds0中!
        int reLog1 = logService.add(log1);
        // 改日志2
        LogDo log2 = new LogDo();
        log2.setId("222");
        log2.setTime("2025-02-02 11:11:11");
        log2.setContent("testDbDivdeButInOneDb");
        log2.setDepartId("1");//在库ds1中!
        int reLog2 = logService.add(log2);
        // 抛出异常
        if (true) {
            throw new Exception("err");
        }
        return reLog1 + reLog2;
    }

实测证明依然回滚。

3.4 涉及分库表,且运行中某库停机

我们启动项目后,将ds1停止运行,然后测试,依然可以。

4. 小结

经查询文档,在不指定TransactionType,默认采用的TransactionType.LOCAL枚举值,这个用法已经够用了。

可以通过@ShardingTransactionType(TransactionType.XA)改为其他事务类型。


网站公告

今日签到

点亮在社区的每一天
去签到