Как обойти кэш операторов JDBC при одновременной пакетной обработке? - PullRequest
1 голос
/ 25 августа 2009

Я разрабатываю сервер, который должен получать ночные отчеты от сотен подразделений. В настоящее время отчеты представляют собой зашифрованные csv-файлы. В общей сложности отчеты должны составлять от 500 000 до 1 000 000 записей каждый день, которые сохраняются в базе данных для последующего использования.

Я создал набор PreparedStatements для каждой передачи. Эти операторы используются для пакетирования 50 записей перед выполнением и фиксацией. Каждая запись может вызывать до 20 вставок базы данных. Все хорошо, когда передачи ставятся в очередь и обрабатываются один за другим.

Когда я пытался сделать это одновременно, я заметил, что разные потоки получили одинаковые экземпляры PreparedStatements. Это вызвало следующую проблему

  1. Несколько потоков добавили операторы в один пакет
  2. Пакеты выполнялись, когда один из потоков решил, что пришло время сделать это
  3. Коммит был вызван, когда база данных не соответствовала ограничениям, поскольку некоторые потоки не успели использовать некоторые операторы

Вопрос: Есть ли способ заставить подготовленный оператор быть созданным вместо повторного использования существующего из кэша операторов?

Если нет лучшего способа справиться с ситуацией, чем

  • создание отдельного источника данных для пакетов, у которых нет пула операторов / соединений
  • удаление ограничений из базы данных; порядок вставки больше не имеет значения
  • принудительная последовательная обработка

Редактировать: попытка выяснить проблему

Пусть будут темы T1 и T2. Пусть будут подготовлены утверждения S1 и S2. Пусть будут партии B1 и B2.

Каждый раз, когда используется S1, он добавляется к B1. Каждый раз, когда используется S2, он добавляется в B2. При фиксации S1 должен быть зафиксирован до S2 для ограничения внешнего ключа.

Проблема возникает, когда

  • T1 радостно обрабатывает передачи
  • T2 обрабатывает передачи невинно
  • T1 использует оператор S1, добавляя s1a к пакету B1, содержащему s1a
  • T1 использует оператор S2, добавляя s2a в пакет B2, содержащий s2a
  • T1 решает, что пора совершать
  • T1 фиксирует партию B1, содержащую s1a
  • T2 использует S1, добавляя s1b в пакет B1, содержащий s1b
  • T2 использует S2, добавляя s2b к партии B2, содержащей s2a, s2b
  • T1 фиксирует партию B1, содержащую s2a, s2b
  • База данных говорит «нет, нет», поскольку s2b передается перед s1b, что запрещено во внешнем ключе.

Этого можно избежать с помощью ручной синхронизации, а также указать в ответах, но тогда мне все равно придется отдельно отслеживать размер каждого пакета вместо применения локальной логики для каждого потока.

Ответы [ 3 ]

1 голос
/ 25 августа 2009

Решение зависит от поставщика.

Если ваш код выполняется под сервлетом, вы можете решить свою проблему, настроив источник данных в своем веб-приложении. Я сделал это с драйвером Oracle под Tomcat, но я уверен, что другие серверы приложений имеют аналогичные способы настройки пула соединений.

Если ваш код автономен, вам придется использовать специфичный для поставщика API. Поскольку вы будете использовать Oracle в качестве своей производственной базы данных, вот краткий пример для драйвера JDBC Oracle:

import oracle.jdbc.OracleConnection;

...

public static void disableStatementCaching(java.sql.Connection conn)
        throws SQLException {
    ((OracleConnection)conn).setImplicitCachingEnabled(false);
}

...

Для получения дополнительной информации см. Руководство разработчика JDBC для Oracle 10.2

1 голос
/ 25 августа 2009

Вы пытаетесь использовать несколько операторов из одного экземпляра соединения? IMO, пул соединений рекомендуется для поведения, которое вы описываете. Альтернативой является синхронизация вручную.

0 голосов
/ 28 августа 2009

Мое текущее решение - перестать беспокоиться и начать любить общие пакеты. Я разделил алгоритм обработки на две фазы

  1. Разобрать набор из N записей и сохранить их в промежуточном формате
  2. Сохранять набор из N записей в виде пакета, когда блокировка присваивается текущему потоку

Это позволяет выполнять параллельный и последовательный анализ. Мне просто нужно найти подходящее место, чтобы минимизировать время ожидания между потоками.

Поиск подходящего места может привести к реализации какой-то двухфазной схемы блокировки, т. Е. Позволить каждому потоку делать то, что ему нравится, и при фиксации, убедиться, что все потоки завершили свою текущую запись до фактического пакетного выполнения.

В последнем решении может потребоваться синхронизация установки параметров для каждого PreparedStatement, хотя я не проверял, вызывает ли это какие-либо проблемы. Стоит.

...