我拆过的坑cycx

查询一下!

MyBatis-plus批量写入数据方法saveBatch速度很慢原因排查

问题场景:

使用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,测试发现速度果然提升


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Powered By Z-BlogPHP 1.7.3

Copyright Your WebSite.Some Rights Reserved.