Oracle отставание между коммитом и выбором - PullRequest
8 голосов
/ 12 июня 2009

У нас есть приложение рабочего процесса Java, которое использует базу данных Oracle для отслеживания своих шагов и взаимодействия с другими сервисами. Во время выполнения рабочего процесса выполняется несколько операций вставки / обновления / выбора, и время от времени выборка не возвращает обновленные данные, даже если фиксация вставки / обновления выполнялась до ее успешного завершения. После выхода ошибок рабочего процесса (из-за неверных данных), если мы вернемся и проверим базу данных через стороннее приложение, появятся новые / обновленные данные. Кажется, что существует задержка между тем, когда наши коммиты проходят и когда они видны. Это происходит примерно в 2% всех рабочих процессов и увеличивается при интенсивном использовании базы данных.

Наша группа поддержки баз данных предложила изменить параметр max-commit-пропаганда-delay на 0, так как он по умолчанию равен 700. Это казалось возможным решением, но в конечном итоге не решило нашу проблему.

Приложение работает в WebSphere, а база данных Oracle настроена как источник данных JDBC. Мы используем Oracle 10.1g. Приложение написано на Java 1.5.

Любая помощь будет оценена.

редактировать: пример кода

DataSource ds; // spring configured

String sql = "INSERT INTO " + currentTable + " (" + stepId + ',' + stepEntryId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " ) VALUES (?, ?, ?, null, ?, ?, ?, null, ?, null)";

Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
// set values
stmt.executeUpdate();
// close connections

// later on in the code...
Connection conn = ds.getConnection();
PreparedStatement stmt = null;
ResultSet rset = null;

String sql = "SELECT " + stepId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " FROM " + currentTable + " WHERE " + stepEntryId + " = ?";
stmt = conn.prepareStatement(sql);

stmt.setLong(1, entryId);

rset = stmt.executeQuery();
//close connections

Ответы [ 8 ]

6 голосов
/ 12 июня 2009

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

  1. Используете ли вы какие-либо параметры WRITE в команде COMMIT? Если нет, подтвердите значение вашего параметра инициализации COMMIT_WRITE. Если вы используете «WRITE BATCH» или, особенно, «WRITE BATCH NOWAIT», вы можете открыть себя для проблем параллелизма. «WRITE BATCH NOWAIT» обычно используется в случаях, когда скорость ваших транзакций записи имеет большее значение, чем возможные проблемы параллелизма. Если ваш параметр инициализации использует варианты «ЗАПИСЬ», вы можете переопределить его на основе транзакции, указав в ваших коммитах предложение IMMEDIATE ( см. COMMIT )

  2. Является ли транзакция, которая пытается прочитать данные, вызывающей SET TRANSACTION до совершения другой транзакции? Использование SET TRANSACTION для указания ТОЛЬКО ДЛЯ ЧАСТИ SERIALIZATION READ или SERIALIZABLE приведет к тому, что транзакция не увидит изменений, происходящих из других зафиксированных сеансов, которые произошли после вызова SET TRANSACTION ( см. SET TRANSACTION )

edit: я вижу, что вы используете класс DataSource. Я не знаком с этим классом - я предполагаю, что это ресурс обмена подключениями. Я понимаю, что ваш текущий дизайн приложения может не облегчать использование одного и того же объекта соединения в течение всего рабочего процесса (шаги могут быть разработаны для независимой работы, и вы не создали средство для передачи объекта соединения от одного шага к далее), но вы должны убедиться, что объекты подключения, возвращаемые объекту DataSource, являются «чистыми», особенно в отношении открытых транзакций. Может случиться так, что вы не вызываете SET TRANSACTION в своем коде, но другой потребитель DataSource в другом месте может сделать это и вернуть соединение обратно к источнику данных, когда сеанс все еще находится в режиме SERIALIZABLE или READ ONLY. При совместном использовании подключений необходимо выполнить откат всех подключений, прежде чем передать их новому потребителю.

Если у вас нет контроля или видимости поведения класса DataSource, вы можете попытаться выполнить ROLLBACK для вновь полученного соединения, чтобы убедиться, что у него нет уже установленных транзакций.

4 голосов
/ 12 июня 2009

Если команда администраторов баз данных пыталась изменить параметр max_commit_propagation_delay, это, вероятно, означает, что вы подключаетесь к экземпляру RAC (т.е. несколько разных серверов обращаются к одной базе данных).

В этом случае, когда вы закрываете и снова открываете соединение в своем коде Java, есть вероятность, что вам ответит другой сервер. Параметр задержки означает, что существует небольшой временной интервал, когда два экземпляра не будут находиться в один и тот же момент времени. Ответ, который вы получаете, соответствует моменту времени, но может быть не самым актуальным.

В соответствии с предложением KM, самым простым решением было бы сохранить соединение открытым после фиксации.

В качестве альтернативы вы также можете добавить задержку после закрытия соединения, если это целесообразно (например, если это пакетное задание и время отклика не критично).

1 голос
/ 12 июня 2009

использовать с помощью ORM? это может быть выбор из кэша, а не формирование базы данных после изменения.

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

"несмотря на то, что коммит вставки / обновления выполнялся до его успешного завершения."

Это подсказывает мне, что вы выполняете commit (), а затем ожидаете, что снова прочитаете те же самые данные (это повторяемое чтение).

Это говорит о том, что вы не должны совершать. Пока вы хотите убедиться, что НИКАКАЯ ДРУГАЯ ЗАДАЧА не сможет изменить ЛЮБЫЕ данные, которые вы ОЖИДАЕТЕ, ЧТОБЫ оставаться стабильными, вы не можете позволить себе снимать блокировки (что делает фиксация).

Обратите внимание, что пока вы держите блокировку на каком-либо ресурсе, другие потоки будут накапливаться, "ожидая, пока этот ресурс станет доступным". Вероятность того, что этот стек будет непустым в момент снятия блокировки, возрастает по мере увеличения общей загрузки системы. И что ваша СУБД сделает вывод, когда вы (наконец) выпустите «commit», так это заключите, что «эй, вау, этот парень наконец-то закончил с этим ресурсом, так что теперь я могу позволить всем остальным ожидающим попробовать и сделать их вещь с этим (И НИЧЕГО не препятствует тому, чтобы "их вещь" была обновлением!) ".

Возможно, есть проблемы с изоляцией моментальных снимков Oracle, которые я пропускаю. Извините, если так.

0 голосов
/ 15 июня 2009

Я не вижу коммит в вашем коде. Они являются наиболее важными утверждениями в таком приложении, поэтому я бы хотел, чтобы они были написаны явно каждый раз, не полагаясь на close () или что-то подобное.

Возможно, для ваших подключений по умолчанию для автоматической фиксации также установлено значение true, которое точно объясняет поведение (оно фиксируется после каждой вставки / обновления).

Можете ли вы проверить, что ваши коммиты находятся именно там, где вы хотите, например, в конце транзакции, а не раньше?

Если есть коммиты, когда вы частично проходите, тогда у вас есть условие гонки между вашими потоками, которое также объясняет, почему при увеличении нагрузки возникает больше проблем.

0 голосов
/ 12 июня 2009

Фрагмент кода на самом деле не включает коммит.

Если вы предполагаете / полагаете, что закрывающее соединение выполняет фиксацию, оно может быть не синхронным (т. Е. Java может сообщать о закрытом соединении, когда говорит Oracle закрыть соединение, что означает, что это может быть до фиксации завершено Oracle).

0 голосов
/ 12 июня 2009

Возможным обходным путем может быть использование транзакции JTA. Он держит ваше соединение открытым «за сценой» через несколько открытых / закрытых коннекторов jdbc. Возможно, он будет поддерживать ваше соединение на том же сервере и избежать этой проблемы синхронизации.

UserTransaction transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
transaction.begin();
// doing multiple open/close cxs
transaction.commit();
0 голосов
/ 12 июня 2009

Это похоже на проблему с RAC, с подключениями к двум разным экземплярам, ​​и SCN не синхронизирован.

В качестве обходного пути, не закрывайте соединение с базой данных и не получайте новое, а используйте то же соединение повторно.

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

[ДОБАВЛЕНИЕ]

В своем ответе Стив Броберг (+1!) Выдвигает интересные идеи. Я не учел:

  • COMMIT может быть чем-то отличным от IMMEDIATE WAIT
  • уровень изоляции транзакции может отличаться от READ COMMITTED

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

[/ ДОПОЛНЕНИЕ] * * тысяча двадцать-один

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...