2015-10-21 4 views
1

Я использую Oracle 11g (это может повлиять на решение). У меня версия таблицы, как показано ниже. Я хочу сделать атомарную операцию, состоящую из выбора из этой таблицы, вставить в другую таблицу (в зависимости от состояния прежнего) и обновить состояние прежней таблицы.Атомность оптимистической блокировки

Я хочу простое решение (простая задача простого решения?), Атомная, и я хочу избежать взаимоблокировок. Я выбираю оптимистичную стратегию блокировки.

Итак, у меня есть такая таблица

CREATE TABLE table (
    id int, 
    version int, 
    state varchar(20) 
); 

В псевдокоде у меня есть что-то вроде этого:

Line 1: SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
Line 2: START TRANSACTION; 
Line 3: SELECT state as S, version as V from table where id = X; 
Line 4: if (S == 'TODO') then 
Line 5:  INSERT INTO other_table ... 
Line 6:  UPDATE table SET state = 'DONE', version = version + 1 WHERE id = X and version = V 
Line 7: COMMIT; 

Насколько я понимаю SQL транзакции, другой поток может выполнять один и тот же блок кода между 6-й строке и седьмой первый протектор. А потом (учитывая уровень изоляции = прочитанный) У меня есть две вставки в other_table, которых я не хочу.

Как я могу сделать этот блок кода действительно атомарным?

Я хотел бы избежать блокировки строк и уровня изоляции сериализации (взаимоблокировок).

+0

Если DBMS использует оптимистическое управление параллелизмом вы никогда не получите никаких тупиков. Сделка - это атомная операция. – jarlh

+0

У вас есть определенная РСУБД в виду? –

+0

Да, это Oracle 11g. – emstol

ответ

3

Не нужно возиться с уровнем изоляции, вы можете сначала сделать UPDATE, и если обновление было успешным, сделайте INSERT. Обновление заблокирует затронутую строку (oracle documentation here), поэтому другой сеанс будет заблокирован до тех пор, пока первый сеанс не завершит транзакцию.

Пример с PL/SQL:

BEGIN 
    UPDATE t 
    SET state = 'DONE', version = version + 1 
    WHERE id = x 
    AND state = 'TODO'; 

    IF(SQL%FOUND) THEN 
    dbms_output.put_line('INSERT HERE'); 
    END IF; 
END; 
/
+0

Hm .. Вы уверены, что обновление блокирует затронутую строку до конца транзакции? Вы можете поделиться ссылкой на документацию? – emstol

+0

Несомненно, посмотрите на [Автоматические блокировки в операциях DML] (http://docs.oracle.com/cd/E11882_01/server.112/e41084/ap_locks001.htm#SQLRF55502). Вы также можете попробовать сами, выполнив код в двух сеансах SQL * Plus, не закрывая транзакцию. –

Смежные вопросы