Обработка одновременного запроса при сохранении в базе данных Oracle? - PullRequest
2 голосов
/ 17 сентября 2011

У меня есть такой сценарий, на веб-сайте авиакомпании (с использованием Java) два отдельных клиента отправляют два запроса одновременно, чтобы забронировать одно и то же место в одной и той же авиакомпании
из Нью-Йорка в Чикаго. Я использую базу данных оракула, и уровень изоляции считывается зафиксированным. Мой вопрос здесь заключается в том, что база данных оракула обеспечивает какое-либо решение, чтобы иметь дело с этим типом параллельного сценария? что я знаю, когда первый оператор транзакции DML запущен, он получит блокировку затронутого строк и будет освобожден после завершения транзакции, т. е. при выполнении отката или фиксации. Но как только будет выполнена фиксация, и второй запрос будет продолжен, как только первый завершен и переопределит первый. Так что это не поможет?

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

Ответы [ 4 ]

5 голосов
/ 17 сентября 2011

Будет перезаписано, только если вы позволите.Вы можете попробовать что-то вроде

UPDATE seatTable
SET seatTaken = true
WHERE .. find the seat, flight etc.. AND seatTaken = false

Это вернет 1 строку, обновленную в первый раз, и 0 строк, обновленную после этого.

2 голосов
/ 18 сентября 2011

Как вы упоминаете, настройки трансанкции помогут вам выполнить одну операцию.Лучший способ применить ограничения такого рода - обеспечить, чтобы ваша реляционная модель была вынуждена не принимать 2-ю операцию после успешного выполнения 1-й.

Вместо того, чтобы выполнять обновление строки, скажем, update.... seat = "зято ", создайте таблицу бронирования (клиент, рейс, место) с ограничением (column: seat = unique) (посмотрите документацию, чтобы узнать синтаксис этого при создании таблицы).Таким образом, ваш процесс резервирования становится вставкой в ​​таблицу резервирования, и вы можете положиться на СУБД, чтобы обеспечить соблюдение ваших реляционных ограничений для поддержания работоспособности вашей бизнес-модели.

Например, пусть t1 будет более ранним временем работы, у вас будет:

t1=> insert into reservations(customer1,flight-x,seat-y) // succeeds. Customer 1 reserved the seat-y
t2=> insert into reservations(customer2,flight-x,seat-y) // fails with RDBMS unique constrain violated.

Единственный способ зарезервировать seat-y снова - это сначала удалить предыдущее резервирование, что, вероятно,ваш бизнес-процесс хочет достичь.

1 голос
/ 18 сентября 2011

Помимо выполнения всего в одном UPDATE путем тщательного составления предложения WHERE, вы можете сделать следующее:

Транзакция 1:

  • SELECT ... FOR UPDATE исключительно блокирует строку на время транзакции.
  • Проверьте, занесено ли возвращенное состояние строки, и выйдите (или повторите попытку другой строки), если это так.
  • UPDATE для строки и присвоения ей статуса «забронировано» - тем временем никто не обновил ее.
  • Commit. Это удаляет эксклюзивный замок.

Транзакция 2:

  • SELECT ... FOR UPDATE блокирует до завершения транзакции 1, а затем блокирует только строку.
  • Возвращенный статус строки «забронирован» (поскольку транзакция 1 пометила его таким образом), поэтому выйдите (или, возможно, повторите другую строку).
1 голос
/ 18 сентября 2011

Для управления параллелизмом на веб-сайте обычной практикой является наличие столбца в каждой записи, который позволяет вам проверить, что он не обновлялся с момента его получения. Либо последняя дата обновления, либо последовательный номер версии (автоматически увеличивается с помощью триггера).

Обычно вы читаете данные (плюс столбец параллелизма)

SELECT seat,etc,version_no
FROM t1
WHERE column = a_value

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

(номер версии или дата обновления будет меняться после каждого обновления)

BEGIN
    UPDATE t1
    SET seatTaken = true
    WHERE seatid = .....
    AND version_no = p_version
    RETURNING version_no INTO p_version;
EXCEPTION WHEN NOT_FOUND THEN
    --Generate a custom exception 
    --concurrency viloation the record has been updated already
END;

триггер для автоматического обновления номера версии будет выглядеть примерно так

CREATE OR REPLACE TRIGGER t1_version
AFTER INSERT OR UPDATE ON t1
FOR EACH ROW
BEGIN
    IF :new.version_no IS NULL THEN
       :new.version_no  := 0;
    ELSE
       :new.version_no  := :old.version_no  + 1;
    END IF;
END;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...