2015-09-09 3 views
0

Наше приложение считывает запись из таблицы «Событие» Oracle. Когда существует запись событий, мы обновляем поле «count» этой записи. Если запись не существует, мы ее вставляем. Поэтому мы хотим только 1 запись для определенного события в таблице.Последовательные потоки приложений и незафиксированные данные в Oracle

Проблема с этим, вероятно, вполне предсказуема: один поток приложений будет читать таблицу, увидеть, что события там нет, вставить новое событие и зафиксировать. Но прежде чем он совершит второй поток, он также прочитает таблицу и увидит, что события там нет. И тогда оба потока вставляют событие, и в итоге мы получим 2 записи для одного и того же события.

Я предполагаю, что синхронизация доступа к этому конкретному методу в нашем приложении предотвратит эту проблему, но что является лучшим вариантом в Oracle, чтобы предотвратить это? Будет ли MERGE, например, всегда предотвращать эту проблему?

+0

Merge обеспечит его, и для меня это было бы предпочтительным решением. Обеспечение его в приложении позволит сохранить его только для приложения (а не для других приложений, способных генерировать одни и те же события), но это будет определенно больше кода, менее безопасным и медленным. – Husqvik

+1

Merge не будет защищать его, так как он работает с точно такой же моделью согласованности чтения, как и любая другая инструкция SQL, и в любом случае это не атомная операция. –

+0

@ Husqvik и David - это два диаметрально противоположных ответа :-) На самом деле, я полагаю, Дэвид прав, говоря, что проблема все равно будет существовать, потому что даже используя MERGE, второй запрос MERGE не увидит незафиксированное изменение первого MERGE (aka read_committed), правильно? – Julius

ответ

1

Сериализация доступа к процедуре, которая реализует эту функциональность, была бы тривиальной для реализации, используя DBMS_LOCK для определения и использования исключительной блокировки.

Сериализация по методам на базе SQL практически невозможна из-за модели согласованности чтения.

+0

Обновление всегда сериализуется, а также вставка с уникальным ограничением. – Husqvik

+0

Возможно, есть способ использовать уникальные ограничения и исключения исключений и т. Д., И, конечно же, вам понадобятся уникальные ограничения для защиты данных, когда это возможно. Но предоставление явного механизма сериализации в API является тривиальным и очень легким. –

+0

Я не упоминал об этом в своем вопросе, но нам нужна только одна запись за событие_имя (строка) в день (поле даты). Это было бы трудно зафиксировать в уникальном ограничении. – Julius

1
CREATE TABLE EVENTS (ID NUMBER PRIMARY KEY, COUNTER NUMBER NOT NULL); 

MERGE INTO EVENTS 
USING (SELECT ID, COUNTER FROM DUAL LEFT JOIN EVENTS ON EVENTS.ID = :EVENT_ID) SRC 
ON (EVENTS.ID = SRC.ID) 
WHEN MATCHED THEN UPDATE SET COUNTER = SRC.COUNTER + 1 
WHEN NOT MATCHED THEN INSERT (ID, COUNTER) VALUES (:EVENT_ID, 1); 

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

Он также не производит никаких исключений, связанных с согласованностью данных, поэтому вам не нужна специальная обработка.

UPDATE: он фактически создает уникальное исключение нарушения, если оба потока вставляются. Я думал, что второе слияние переключится на обновление, но это не так.

ОБНОВЛЕНИЕ: Протестировано только одно и то же дело на SQL Server, а при параллельном выполнении и записи нет одной вставки MERGE и вторых обновлений.

+0

Да, как объясняет Том Ките, https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:61865893444475, вам нужно сериализовать, если вы хотите избежать потенциального либо для несогласованности данных, либо для обработки ошибок, порожденных уникальными ограничениями. –

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