READ COMMITTED уровень изоляции базы данных в оракуле

Я работаю над веб-приложением, подключенным к оракулу. У нас есть таблица в оракуле со столбцом «активировано». Только в одной строке этот столбец может иметь значение 1 одновременно. Чтобы обеспечить это, мы использовали уровень изоляции SERIALIZED в Java, однако мы сталкиваемся с ошибкой «невозможно сериализовать транзакцию» и не можем понять, почему.

Нам было интересно, будет ли работать уровень изоляции READ COMMITTED. Итак, мой вопрос заключается в следующем:

Если у нас есть транзакция, которая включает следующий SQL:

SELECT *
FROM MODEL;

UPDATE MODEL
SET ACTIVATED = 0;

UPDATE MODEL
SET ACTIVATED = 1
WHERE RISK_MODEL_ID = ?;

COMMIT;

Учитывая, что более чем одна из этих транзакций может выполняться одновременно, возможно ли, чтобы более чем одна строка MODEL установила флаг активации в 1?

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


person Ben    schedule 22.09.2009    source источник


Ответы (3)


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

Вы также должны убедиться, что строка со значением RISK_MODEL_ID существует (иначе у вас будет нулевая строка со значением «1» в конце вашей транзакции).

Чтобы предотвратить параллельные инструкции INSERT, вы должны БЛОКИРОВКА таблицы (в ЭКСКЛЮЗИВНОМ РЕЖИМЕ).

person Vincent Malgrat    schedule 22.09.2009
comment
Аналогично, но я бы БЛОКИРОВАЛ МОДЕЛЬ В ЭКСКЛЮЗИВНОМ РЕЖИМЕ и выполнил ОБНОВЛЕНИЕ МОДЕЛИ SET ACTIVATED = 0 WHERE ACTIVATED = 1 Обновляется меньше строк, поэтому это немного более производительно, а LOCK TABLE предотвратит одновременную активность в таблице. - person Gary Myers; 24.09.2009

Вы можете рассмотреть возможность использования уникального функционального индекса, чтобы позволить Oracle обрабатывать ограничение наличия только одной строки с активированным флагом, установленным на 1.

CREATE UNIQUE INDEX MODEL_IX ON MODEL ( DECODE(ACTIVATED, 1, 1, NULL));

Это остановит более чем одну строку с флагом, установленным на 1, но не означает, что всегда есть одна строка с флагом, установленным на 1.

person WW.    schedule 22.09.2009

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

declare
    cursor c is 
        select activated
        from model
        where activated = 1
        for update of activated;
    r c%rowtype;
begin
    open c;
    --  this statement will fail if another transaction is running
    fetch c in r;
    ....
    update model
    set activated = 0
    where current of c;

    update model
    set activated = 1
    where risk_model_id = ?;

    close c;

    commit;
end;
/

commit освобождает блокировку.

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

Этот подход гораздо более масштабируем, чем обновление всех строк в таблице. Используйте индекс на основе функций, как показано в WW, чтобы обеспечить соблюдение правила, согласно которому только одна строка может иметь ACTIVATED=1.

person APC    schedule 23.09.2009