Mybaties-Plus saveBatch()、自定义批量插入、多线程批量插入性能测试和对比

一.背景

最近在做一个项目的时候,由于涉及到需要将一个系统的基础数据全量同步到另外一个系统中去,结果一看,基础数据有十几万条,作为小白的我,使用单元测试,写了一段代码,直接采用了MP(Mybaties-Plus)自带的saveBatch()方法,将基础数据导入到新的系统中去,但是后面涉及多次修正基础数据的情况,导致,每次重新插入数据或者更新的时候,都需要花费十几分钟的时间,后面想着以下的方案进行了优化。
其实针对自带的saveBatch()方法插入很慢,一般都是由于数据库连接url上没有配置批量操作的属性,只需要在url上加上如下属性即可,如下:

rewriteBatchedStatements=true

在配置数据库连接信息的时候,配置类似如下:

jdbc:mysql://数据库地址/数据库名?useUnicode=true&characterEncoding=UTF8&allowMultiQueries=true&rewriteBatchedStatements=true

加上之后,你就会发现,saveBatch的速度直线提升,效果还是很不错的,一万条数据估计也就在几百毫秒。
接下来的文章都是设置在rewriteBatchedStatements=false情况下,且MP(Mybaties-Plus)为3.5.3.1版本下进行测试的。

二 .优化方法

如果在 rewriteBatchedStatements=false情况下,使用自带的方法,插入几十万数据是比较慢的,我们先讲解自带的方法,再讲解MP给我们自定义空间的自定义方法,然后在加入一些多线程的情况下进行的测试和方案比较。

2.1 Mybaties-plus自带的批量saveBatch()方法

直接上代码
实体类如下:

@Data
@TableName("test_user")
public class TestUser implements Serializable {
    private String id;
    private String name;
    private String managerId;
    private String salary;
    private String age;
    private String departId;
    private String remark;
    private String province;
}

Mapper如下:

public interface TestUserMapper extends BaseMapper<TestUser> {
}

Service如下:

public interface ITestUserService extends IService<TestUser> {
}

@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements ITestUserService {
}

接下来我使用单元测试的方法,构造200000条数据,测试Mybaties-Plus自带的saveBatch()方法,代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class UserTest {

    @Autowired
    private ITestUserService userService;

    @Test
    public void testInsertBatch(){
        List<TestUser> userList = new ArrayList<>();
        for(int i = 0; i < 199999; i++){
            TestUser user = new TestUser();
            user.setName("张三");
            user.setAge("20");
            user.setProvince("重庆市");
            user.setSalary("200000");
            user.setRemark("diitch");
            userList.add(user);
        }
        long s = System.currentTimeMillis();
        userService.saveBatch(userList);
        System.out.println("保存200000条数据消耗" + (System.currentTimeMillis() - s) + "ms");
    }
    }

测试结果如下,大概需要10s中的时间:
在这里插入图片描述
我们可以跟踪源码,它的实现如下:

  default boolean saveBatch(Collection<T> entityList) {
        return this.saveBatch(entityList, 1000);
    }

 public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

       public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, (sqlSession) -> {
            int size = list.size();
            int idxLimit = Math.min(batchSize, size);
  
单元测试中处理`saveBatch`操作通常是在模拟数据持久化操作或者验证业务逻辑的时候。以下是进行这类测试的一般步骤: 1. **创建测试环境**:首先,你需要创建一个测试数据库上下文,或者使用模拟的对象来代替真正的数据库连接,因为`saveBatch`通常是数据库层面的操作。 ```java Database db = new InMemoryDatabase(); // 如果是内存数据库 List<MyEntity> entities = ...; // 要保存的数据列表 // 或者使用Mockito模拟Repository MyRepository repository = Mockito.mock(MyRepository.class); ``` 2. **设置期望行为**:如果你的`MyRepository`有一个`saveBatch`方法,你可以使用Mockito等工具设置它被调用时的行为,比如返回值、抛出异常等。 ```java Mockito.when(repository.saveBatch(entities)).thenReturn(saveResult); // saveResult是一个模拟的SaveResult对象 ``` 3. **执行代码并验证结果**:现在可以执行待测代码,并检查是否按照预期执行了`saveBatch`操作。 ```java myService.saveEntities(entities); // 这里假设service层调用了repository的saveBatch verify(repository).saveBatch(entities); // 验证mock对象被调用了一次 // 可能还需要验证saveResult,例如数据是否正确保存,错误处理是否恰当 assertThat(saveResult.getSuccessCount(), is(equalTo(entities.size()))); ``` 4. **清理资源**:测试完成后记得关闭模拟的数据库连接或清理其他测试资源。 ```java db.shutdown(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代号diitich

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值