24 ноября 2021
Тэги: Kotlin, PostgreSQL, Spring, Spring Boot, SQL, YouTube.
Давайте рассмотрим, как обеспечить пакетную вставку (batch insert) большого количества данных с помощью Spring JDBC API. Допустим, нам нужно вставить 100 000 строк в таблицу в postgres. В качестве драйвера для работы с БД используем стандартный org.postgresql:postgresql.
Мы рассмотрим несколько вариантов, начиная с самого простого и заканчивая самым быстрым, постепенно улучшая нашу реализацию. Если вам нужен финальный вариант, листайте сразу к последнему.
Данный материал доступен также в формате видео на YouTube.
Допустим у нас есть такая таблица для хранения истории каких-то заказов:
В Kotlin ей будет соответствовать структура:
Наиболее простой вариант вставки с помощью NamedParameterJdbcTemplate:
Здесь мы на каждую из 100 тысяч записей делаем отдельный insert. На моём ноуте с SSD такая реализация при работе с локальной БД выполняется 2 с лишним минуты – это очень долго.
Мы можем значительно улучшить предыдущий вариант, если добавим аннотацию @Transactional. Поскольку запросы будут выполняться в транзакции, это позволит движку СУБД не делать лишних действий.
Также небольшой прирост производительности даст явное указание типов данных с помощью третьего параметра в методе addValue(). Тогда драйвер postgres не будет запрашивать метаданные таблицы.
В итоге данная реализация выполняется у меня где-то за 7 секунд. Прирост скорости на порядок, без значительных изменений кода.
Однако наш драйвер помимо обычной вставки поддерживает также и пакетную. То есть умеет вставлять все данные за один запрос. Для этого мы сначала формируем набор значений в виде типизированного массива для каждой из 100 тысяч строк, а затем вызываем метод batchUpdate():
Данная реализация выполняется ещё быстрее: примерно за 2.5 – 3 секунды. По сравнению с предыдущим, этот вариант даёт прирост скорости более чем в 2 раза. Наличие транзакции в данном варианте прироста особо не даёт, что вполне ожидаемо.
Я допускаю, что на скорость вставки также могут влиять различные настройки самой БД, поэтому замеры производил на стандартной версии postgres, которую вообще никак не настраивал.
Исходя из сказанного выше можно рекомендовать использовать batchUpdate() при вставке большого количества строк в таблицу. Если же этот вариант по каким-то причинам не подходит, тогда можно обернуть обычный update() в транзакцию – это также даст существенный прирост в скорости. Дополнительный прирост может дать явное указание типов данных для каждого параметра запроса.
Kotlin, Java, Spring, Spring Boot, Spring Data, SQL, PostgreSQL, Oracle, Linux, Hibernate, Collections, Stream API, многопоточность, файлы, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.