Блокировка базы данных при использовании Rownum? - PullRequest
0 голосов
/ 04 февраля 2009

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

  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;

Ответы [ 3 ]

2 голосов
/ 05 февраля 2009

Чтобы возникла тупиковая ситуация, у вас должны быть следующие два условия.

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

  2. Замки необходимо захватывать в другом порядке.

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

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

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

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

1 голос
/ 05 февраля 2009

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

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

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

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

Если вы на 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)
1 голос
/ 04 февраля 2009

Существует определенный риск взаимоблокировки, если вы используете несколько ОБНОВЛЕНИЙ для одной и той же таблицы.

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

Чем больше времени занимает ОБНОВЛЕНИЕ, тем выше риск тупика.

...