Зависание обновления Oracle
У меня проблемы с обновлением Oracle. Вызов ExecuteNonQuery зависает бесконечно.
Код:
using (OracleCommand cmd = new OracleCommand(dbData.SqlCommandStr, conn))
{
foreach (string colName in dbData.Values.Keys)
cmd.Parameters.Add(colName, dbData.Values[colName]);
cmd.CommandTimeout = txTimeout;
int nRowsAffected = cmd.ExecuteNonQuery();
}
CommandTimeout устанавливается в 5, а параметры устанавливаются в маленькие целочисленные значения.
Запрос:
UPDATE "BEN"."TABLE03" SET "COLUMN03"=:1,"COLUMN04"=:2 WHERE COLUMN05 > 0
Запрос выполняется быстро из sqlplus и обычно выполняется быстро из моего кода, но время от времени он зависает навсегда.
Я выполнил запрос к v$locked_object, и есть одна запись, ссылающаяся на эту таблицу, но я думаю, что это обновление не завершается.
Я хотел бы знать две вещи: что может привести к зависанию обновления?
Что еще более важно, почему здесь не выдается исключение? Я ожидаю, что звонок подождет пять секунд, а затем тайм-аут.
6 ответов
Я столкнулся с этим из-за рейтинга страницы в результатах поиска.
В моем случае это было потому, что я выполнил запрос в SqlPlus, но я забыл его зафиксировать. В этом случае, как сказал Винсент, строка была заблокирована в другом сеансе.
Передача обновления SqlPlus устранила проблему.
Когда простое обновление зависает, это часто означает, что вы заблокированы другим сеансом. Oracle не разрешит более одной транзакции для обновления строки. Пока транзакция не зафиксировала или не откатила свои изменения, она будет блокировать строки, которые она обновила / удалила. Это означает, что другому сеансу придется ждать, если они захотят изменить те же строки.
Вы должны ВЫБРАТЬ... ДЛЯ ОБНОВЛЕНИЯ ОБНОВЛЕНИЯ до того, как ОБНОВИТЬ, если вы не хотите зависать бесконечно.
У меня была похожая проблема, которая была вызвана командой Sql, которая не была зафиксирована - я предполагаю, что программа в какой-то момент вылетела в середине.
Вот как я исправил свою проблему:
Сначала откройте SqlPlus и сделайте коммит, чтобы устранить проблему.
Затем измените код для подтверждения транзакции или отката в случае возникновения исключения. Это предотвратит повторное возникновение проблемы.
Вы можете изменить свой код на что-то вроде этого:
using (OracleTransaction transaction = conn.BeginTransaction())
{
using (OracleCommand cmd = new OracleCommand(dbData.SqlCommandStr, conn))
{
foreach (string colName in dbData.Values.Keys)
cmd.Parameters.Add(colName, dbData.Values[colName]);
cmd.CommandTimeout = txTimeout;
try
{
int nRowsAffected = cmd.ExecuteNonQuery();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
}
}
Вы можете увидеть, какое событие ожидает ваш сеанс, запросив V$SESSION_WAIT (после определения SID сеанса, возможно, посмотрев на V$SESSION). Если событие что-то вроде "постановки в очередь", вы ожидаете блокировки, удерживаемой другим сеансом, что в данном случае кажется вероятным объяснением.
Я часто сталкивался с этой проблемой и не только с запросами на обновление (особенно с запросами INSERT INTO...SELECT FROM). Это на Oracle 9i.
Я нашел решение, поэтому решил найти эту связанную тему SO: В строке соединений установите:
Pooling=False
в строке подключения. Полная рабочая строка подключения может выглядеть так:
DATA SOURCE=k19.MYDOMAIN.com/plldb;PERSIST SECURITY INFO=True;Pooling=False;USER ID=IT;Password=SECRET
Предостережения. Если для пула задано значение false, ваш запрос будет защищать новое соединение при каждом его запуске. Запросы, которые выполняются очень часто, могут иметь снижение производительности по сравнению с тем, что было бы, если бы ODP.NET были надежными. Однако, учитывая проблему, работать немного медленнее, чем зависать.
Похоже, что база данных ожидает фиксации / отката, поэтому она блокирует строку. Я бы предложил добавить
int nRowsAffected = cmd.ExecuteNonQuery();
cmd.Commit();