READ COMMITTED уровень изоляции базы данных в Oracle - PullRequest
5 голосов
/ 23 сентября 2009

Я работаю над веб-приложением, связанным с Oracle. У нас есть таблица в оракуле с колонкой «активировано». Только одна строка может иметь этот столбец равным 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?

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

Ответы [ 3 ]

3 голосов
/ 23 сентября 2009

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

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

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

3 голосов
/ 23 сентября 2009

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

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

Для предотвращения одновременных операторов INSERT вы должны LOCK таблица (в ИСКЛЮЧИТЕЛЬНОМ РЕЖИМЕ).

2 голосов
/ 23 сентября 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.

...