问题场景:

使用MyBatis-plus的saveBatch方法执行数据批量insert

问题描述:

    /**
     * 批量添加设备
     * @param deviceList
     * @param applicationName
     * @return
     */
    public boolean saveBatchDevice(List<Device> deviceList, String applicationName) {
        if (CollectionUtils.isEmpty(deviceList)) {
            return false;
        }
        boolean result = saveBatch(deviceList);
        if (result) {
            List<Map> mapList = deviceList.stream().map((Function<Device, Map>) device -> {
                Map<String, String> item = new HashMap<>(5);
                item.put("imei", device.getImei());
                item.put("supplier", device.getSupplier());
                item.put("model", device.getModel());
                return item;
            }).collect(Collectors.toList());
            kafkaTemplate.send(String.format(ADD_DEVICE_TOPIC, applicationName), JSONUtil.toStr(mapList));
        }
        return result;
    }
调用该方法对传入的设备信息列表进行批量保存,但是发现速度非常慢,传入1000条数据保存用了大概1分钟,通过使用StopWatch记录各个流程的耗时,发现所有耗时全部在saveBatch上,这是该Service继承MyBatis-plus的ServiceImpl类后,获得的批量保存数据的方法。

原因分析:

查看该saveBatch源码实现
    /**
     * 执行批量操作
     *
     * @param entityClass 实体类
     * @param log         日志对象
     * @param list        数据集合
     * @param batchSize   批次大小
     * @param consumer    consumer
     * @param <E>         T
     * @return 操作结果
     * @since 3.4.0
     */
    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");
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
            int size = list.size();
            int i = 1;
            for (E element : list) {
                consumer.accept(sqlSession, element);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
    }
将传入的实体List分为1000个一批,每个调用sqlSession.insert(sqlStatement, entity),insert完一批做一次sqlSession.flushStatements(),看起来是没有问题,但是就是速度非常慢。查阅相关资料发现,要批量执行的话,JDBC连接URL字符串中需要新增一个参数:rewriteBatchedStatements=true

MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效


解决方案:

在jdbc连接url最后加上jdbc:mysql://172.29.1.100:5000/bd_cloud_vehicle_dev?rewriteBatchedStatements=true,测试发现速度果然提升

————————————————
解决方案参考博文:https://blog.csdn.net/qq_34283987/article/details/107694587

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐