【问题标题】:INSERT into database takes too much time插入数据库需要太多时间
【发布时间】:2019-01-28 09:48:55
【问题描述】:

我创建了一个应用程序,并尝试将 100 条记录插入到我的数据库 (MariaDB) 中,大约需要 20 秒。如何加快这个操作?

我正在使用休眠,我的期望是在最多 2 分钟内插入大约 10k。

        private Person getPerson(ExternalPerson externalPerson) {
        Person person = new Person();

        person.setName(externalPerson.getFirstName());
        person.setLastName(externalPerson.getLastName());
        person.setAdditionalInfo(externalPerson.getIdentifier());
        person.setCountries(Arrays.asList(storeCountryIfNotExist(externalPerson.getCountry())));
        person.setGender(storeGenderIfNotExist(externalPerson.getGender()));

        personRepository.saveAndFlush(person);
        return person;
    }


    private Gender storeGenderIfNotExist(String gender) {
        Gender genderTemp = genderRepository.findByName(gender);
        if (genderTemp != null) {
            return genderRepository.findByName(gender);
        }
        Gender newGender = new Gender();
        newGender.setName(gender);
        return genderRepository.saveAndFlush(newGender);
    }

    private Country storeCountryIfNotExist(String country) {
        Country countrytemp = countryRepository.findByName(country);
        if (countrytemp != null) {
            return countrytemp;
        }
        Country newCountry = new Country();
        newCountry.setName(country);
        return countryRepository.saveAndFlush(newCountry);
    }

【问题讨论】:

  • 不确定到底发生了什么...但是如果您要执行批量操作,我想您最终需要保存并刷新。我也不太确定写入一个表是否需要从另一个表读取(在你的情况下,就是......
  • 避免刷新,不要加载任何东西;让数据库处理重复项。
  • 什么是findByName,为什么要第二次调用它,什么时候有结果值?
  • 向我们展示生成的 SQL 语句。

标签: java mysql hibernate spring-boot mariadb


【解决方案1】:

查看您的代码,我可以看到您正在执行 3 次插入来创建记录。这意味着,您首先使用storeCountryIfNotExist 插入国家/地区记录,然后使用storeGenderIfNotExist 插入性别记录,发布这两个,您最终插入Person 记录。这实际上由 3 个 I/O 操作组成,因为您每次都与插入一起刷新。

为了提高性能,您应该尽量减少 I/O 操作的计数。首先要做的是在插入 3 条记录后只刷新一次。我还没有使用过 MariaDB,但与任何其他数据库一样,MariaDB 应该公开一个批量插入 API,而不是循环,您可以利用该 API 批量插入 10K 记录。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    不要使用someRepository.saveAndFlush(someEntity),而是使用someRepository.save(someEntity),在插入所有记录后,您可以调用someRepository.flush()

    saveAndFlush 方法会在每次插入后立即推送您的更改,这会增加数据库往返次数,从而导致性能下降。

    编辑:添加示例代码 sn-p。

    for ( int i=0; i<10000; i++ ) {
        Person person = new Person(.....);
        session.save(person);
    
        if ( i % 50 == 0 ) { //50 is the batch size, which can be adjusted to find the sweet spot
            //instead of flushing after every save, flush batch of 50 updates together
            session.flush();
            session.clear();
        }
    }
    

    【讨论】:

    • 如何知道何时插入完成并调用 .flush?
    • 我已更新答案以包含示例代码 sn-p。所以基本上在你调用flush之前,hibernate会将你的更改保存在本地内存中。因此,根据您的系统资源和数据,您可以调整值“50”来微调性能。如果您仍有疑问,请告诉我。
    • 谢谢你的回答,我有计划今天完成它;)
    猜你喜欢
    • 2020-06-15
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 2018-11-28
    相关资源
    最近更新 更多