Медленная вставка на PostgreSQL с использованием JDBC

Я работаю над системой, которая загружает данные из облачной системы в локальную базу данных (PostgreSQL, MySQL,...). Теперь у меня проблема с производительностью PostgreSQL, потому что для ввода данных требуется много времени.

Число столбцов и размер данных могут различаться. В примере проекта у меня есть таблица с ок. 170 столбцов. Существует один уникальный индекс - но даже после падения индекса скорость вставки не изменилась.

Я использую драйвер JDBC для подключения к базе данных, и я вставляю данные партиями из 250 строк (используя NamedParameterJdbcTemplate).

Мне потребовалось ок. 18 секунд, чтобы вставить данные в Postgres. Тот же набор данных на MySQL занял у меня всего лишь секунду. Это огромная разница - откуда она взялась? Является ли драйвер Postgres JDBC медленным? Может ли он быть настроен каким-то образом, чтобы сделать его быстрее? Я что-то пропустил? Разница между Postgres и MySQL настолько велика. Любые другие идеи, как сделать это быстрее?

Я сделал пример проекта, который доступен на Github - https://github.com/varad/postgresql-vs-mysql. Все происходит в классе LetsGo в методе "run".

Ответ 1

Кажется, что это комбинация Spring "bug" и драйвера "ошибка".

Spring пытается определить тип данных столбца каждый раз, когда вызывается setValue(). Он делает это, вызывая PreparedStatementMetaData.getParameterMetaData()

Это, по-видимому, приводит к отправке инструкции "подготовить" в базу данных, которая сама по себе довольно быстро (не более 1 мс на моем ноутбуке), но поскольку она вызывается для каждого столбца для каждой строки это суммирует до большого количества времени (он требует для каждого значения, не равного нулю, что приводит к примерно 23 000 вызовов)

В некоторой степени это скорее ошибка Spring, а ошибка драйвера, потому что не кэширование метаданных параметра не имеет смысла (по крайней мере, на мой взгляд). Драйвер MySQL JDBC не поддерживает getParameterMetaData() и Spring знает об этом, и поэтому эта "ошибка" не отображается с MySQL, потому что Spring никогда не вызывает этот метод.

Я не уверен, что поведение драйвера JDBC Postgres можно классифицировать как ошибку, но было бы неплохо, если бы драйвер выполнял кэширование этих метаданных после первого вызова.

Spring может быть убежден, что не получить метаданные оператора через свойство spring.jdbc.getParameterType.ignore

Итак, поставив:

System.setProperty("spring.jdbc.getParameterType.ignore", "true");

перед строка:

LetsGo letsGo = new LetsGo();

это поведение отключено.

Свойство должно быть установлено , прежде чем Spring будет инициализирован.

Когда я делаю это с вашим образцовым проектом, вставка работает на 500 мс на моем ноутбуке.


Изменить

После просмотра комментария относительно использования драйвера Postgres-NG я выкопал источники "официального" драйвера и драйвера NG, а драйвер NG скрывает метаданные параметров после первого вызова, тогда как официальный драйвер не объясняет, почему использование драйвера NG намного быстрее (без отключения вызова в Spring)

Ответ 2

попробуйте использовать драйвер pgjdbc-ng, а затем сравните ваши результаты.

Он доступен здесь: http://impossibl.github.io/pgjdbc-ng/

Ответ 3

В строке подключения добавьте следующее:

&useServerPrepStmts=false&rewriteBatchedStatements=true

Ответ 4

Надеюсь, вы используете пул соединений DB. Вы можете попробовать C3P0. Spring (JDBCTemplate) не предоставляет реализацию пула подключений.