2009-02-04 10 views
0

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

FUNCTION reserveWork(in_batch_id NUMBER, 
         in_work_size NUMBER, 
         in_contentType_id NUMBER) RETURN NUMBER IS 
    rows_reserved NUMBER := 0; 

    BEGIN 
    UPDATE 
      D_Q1 
    SET 
      DQ1_BAT_ID = in_batch_id 
    WHERE 
     DQ1_BAT_ID is null 
     AND DCT_ID = in_contentType_id 
     AND ROWNUM < (in_work_size + 1); 

    rows_reserved := SQL%ROWCOUNT; 

    RETURN (rows_reserved); 

    END; 

ответ

2

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

  1. Каждая транзакция должна состоять из нескольких замков.

  2. Замок должен быть схвачен в другом порядке.

Условие 1 истинно, потому что каждый из ваших потоков блокирует несколько строк. Условие 2 верно в теории, потому что порядок возвращаемых строк не является детерминированным. Например, поток 1 может попытаться обновить строки 1,2,3, а поток 2 может попытаться обновить строки 3,2,1.

На практике Oracle всегда может возвращать строки в том же порядке, чтобы он никогда не заходил в тупик. В любом случае, будьте готовы обработать ошибку ORA-00060 и повторно отправить запрос.

Другая идея - сделать это в два этапа. Первая процедура выполняет SELECT * WHERE ... FOR UPDATE NO WAIT для блокировки строк, если это не возвращает ORA-00054, вторая процедура выполняет фактическое обновление. В противном случае вы повторите попытку.

В любом случае, убедитесь, что INITTRANS в вашем CREATE TABLE установлен на столько клиентов, сколько будет одновременно обновляться.

+0

Ничего себе, я прочитал ответ Тома Ките, упомянутый в ответе Гэри ниже. Очень круто! Это означает, что если блокировка транзакций будет перезапущена, значит, он не должен блокироваться. –

1

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

В частности, поскольку я не вижу COMMIT или ROLLBACK в вашем коде? Я полагаю, это делается в JDBC?

Чем дольше UPDATE, тем выше будет риск взаимоблокировки.

+0

Да, фиксация/откат выполняется сразу после вызова этой функции на основе, если исключение было создано или нет. – Adam

1

Тупик возникает, когда сделка A блокирует запись, то должен ждать транзакции B, чтобы разблокировать запись, в то время как транзакция B ожидает на запись уже блокированной транзакции A.

Oracle имеет довольно сложный механизм обработка изменений таблиц во время обновления. См

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:11504247549852 

В целом, риск тупиковой увеличивает дольше транзакция выполняется и тем больше данных изменяется транзакций. Я бы сказал, что это вряд ли затормозит, но, скорее всего, будет «очереди» - если у вас три или четыре параллельных сеанса, выполняющих этот SQL, каждый сеанс будет иметь одинаковый путь выполнения для SQL, будет идентифицировать те же строки для обновления, один дойдет до них, остальные будут ждать. Когда эта первая транзакция завершится, другая снова захватит записи, найдет их измененными и перезапустит, как описано в статье Tom Kyte, и выберите следующую группу строк.

Если вы находитесь на 11g, вы можете использовать SKIP LOCKED. Он присутствует, но недокументирован, в более ранних версиях. Таким образом, это будет ИСПОЛЬЗОВАТЬ НА ВАШ СОБСТВЕННЫЙ РИСК.

http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/statements_10002.htm#SQLRF01702 

Таким образом, вы

SELECT primary_key BULK COLLECT INTO pk_variable_array FROM D_Q1 
WHERE DQ1_BAT_ID is null 
AND DCT_ID = in_contentType_id 
AND ROWNUM < (in_work_size + 1) 
FOR UPDATE SKIP LOCKED; 
-- 
FORALL i in 1..pk_variable_array 
UPDATE D_Q1 
SET DQ1_BAT_ID = in_batch_id 
WHERE primary_key = pk_variable_array(i)