Запросы уникальных записей в параллельных запросах с помощью Spring - PullRequest
0 голосов
/ 27 мая 2020

Я пытаюсь реализовать блокировку базы данных Oracle в приложении Spring Boot. Вот пример использования: служба должна запросить БД для получения списка записей, соответствующих некоторым критериям, например,

select * from books where book_status='available';

Этот запрос возвращает список всех available книг, и мы просто возвращаем первую книгу из списка, а затем обновите book_status до unavailable:

update books set book_status='unavailable' where book_name='testName';

Теперь в параллельных запросах есть шанс, что 2 запроса могут вернуть одну и ту же книгу, чего никогда не должно происходить - мы всегда должны возвращать разные книги .

Для этого я пытался использовать select for update запросы:

select * from books where book_status='available' for update;

Но этот подход не помог. Несколько параллельных запросов вернули одну и ту же запись.

Я также пытался использовать JPA для блокировки записей, но это тоже не помогло:

Query query = entityManager.createQuery("from Books where book_status='available'");
query.setLockMode(LockModeType.OPTIMISTIC);
query.getResultList();

Может ли кто-нибудь помочь выяснить, как я всегда могу получать разные записи в параллельных запросах?

1 Ответ

0 голосов
/ 27 мая 2020

Я не уверен на 100%, что происходит с Select for update, возвращающим те же строки. Но это моя текущая ментальная модель того, что происходит:

Первый выбор / поток выбирает и блокирует строки. Второй выбирает те же строки и пытается получить блокировку, но не может, пока первая не зафиксируется. Я ожидал, что он увидит обновленную версию строки и не вернет ее. Но в любом случае вы блокируете, что, кажется, не имеет особого смысла для вашего случая.

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

Что вам нужно сделать, так это отметить книги как обрабатываемые заданным процессом. Это можно сделать с помощью следующего процесса:

Обновить первые N строк как обрабатываемые текущим заданием. Подойдет следующий оператор:

update books b
    set book_status = 'processed by ' || :jobname
    where id in (
        select id
        from books b2 
        where book_status = 'available'
        ORDER BY name
        NEXT 10 ROWS ONLY
    ) 

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

Теперь основная часть задания может выбирать отмеченные строки без вмешательства других заданий, пока не будет установлено book_status на unavailable или обратно на available, если вы хотите снова сделать книгу доступной для других заданий.

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

Этот вопрос касается той же основной c проблемы, но с немного другой точки зрения.

...