Как заблокировать выбор и снять блокировку после фиксации обновления с помощью spring?

Я начал использовать весну с последних нескольких месяцев, и у меня есть вопрос о транзакциях. У меня есть метод java внутри моего пакетного задания spring, который сначала выполняет операцию выбора, чтобы получить первые 100 строк со статусом «НЕ ЗАВЕРШЕНО», и выполняет обновление выбранных строк, чтобы изменить статус на «В ПРОГРЕССЕ». Поскольку я обрабатываю около 10 миллионов записей, я хочу запустить несколько экземпляров моего пакетного задания, и каждый экземпляр имеет несколько потоков. Для одного экземпляра, чтобы убедиться, что два потока не извлекают один и тот же набор записей, я сделал свой метод синхронизированным. Но если я запускаю несколько экземпляров своего пакетного задания (несколько JVM), существует высокая вероятность того, что один и тот же набор записей может быть получен обоими экземплярами, даже если я использую «оптимистическую» или «пессимистическую блокировку» или «выбрать для обновления», поскольку мы не можем блокировать записи во время выбора. Ниже показан пример. Транзакция 1 извлекла 100 записей, а тем временем транзакция 2 также извлекла 100 записей, но если я включу блокировку, транзакция 2 будет ждать, пока транзакция 1 не будет обновлена ​​и зафиксирована. Но транзакция 2 снова делает то же самое обновление.

Есть ли способ весной заставить операцию выбора транзакции 2 дождаться завершения выбора транзакции 1?

Transaction1        Transaction2
fetch 100 records   
                    fetch 100 records
update 100 records



 commit         
                    update 100 records
                    commit



@Transactional
public synchronized List<Student> processStudentRecords(){
List<Student> students = getNotCompletedRecords();
if(null != students && students.size() > 0){
    updateStatusToInProgress(students);
}
return student;
}

Примечание. Я не могу сначала выполнить обновление, а затем выбрать. Буду признателен, если будет предложен какой-либо альтернативный подход?


person Tech Guy    schedule 27.02.2019    source источник
comment
Вы случайно не решили эту проблему из самого приложения? Прошел год с тех пор, как вы разместили вопрос, и я до сих пор не могу найти ответ, чтобы добиться этого с весны.   -  person railomaya    schedule 06.05.2020


Ответы (1)


Синхронизация транзакций должна быть предоставлена ​​серверу базы данных и не должна управляться на уровне приложения. С точки зрения сервера базы данных, независимо от того, сколько у вас JVM (потоков), это параллельные клиенты базы данных, запрашивающие операции чтения/записи. Не стоит утруждать себя такими заботами.

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

если я запускаю несколько экземпляров моего пакетного задания (несколько JVM), существует высокая вероятность того, что один и тот же набор записей может быть получен обоими экземплярами, даже если я использую «оптимистическую» или «пессимистическую блокировку» или «выбор для обновления», поскольку мы не может блокировать записи во время выбора

Разделение данных преднамеренно устраняет все эти проблемы. Если вы даете каждому экземпляру набор данных для работы, нет никаких шансов, что работник выберет те же самые записи другого работника. Майкл привел подробный пример в этом ответе: https://stackoverflow.com/a/54889092/5019386.

(Логично) Разделение, однако, не решит проблему конкуренции, поскольку все рабочие потоки будут читать/писать из/в одну и ту же таблицу, но это характер проблемы, которую вы пытаетесь решить. Я говорю, что вам не нужно начинать блокировать/разблокировать таблицу в вашем дизайне, оставьте это базе данных. Некоторые серверы баз данных, такие как Oracle, могут записывать данные одной и той же таблицы в разные разделы на диске для оптимизации одновременного доступа (что может помочь, если вы используете разделение), но опять же, это бизнес Oracle, а не бизнес Spring (или любой другой платформы).

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

  • Шаг 1 (последовательно): копирование/разделение необработанных данных во временные таблицы (последовательно)
  • Шаг 2 (параллельно): запустите несколько рабочих процессов для этих таблиц вместо исходной таблицы с миллионами строк.
  • Шаг 3 (последовательно): копирование/обновление обработанных данных обратно в исходную таблицу

Шаг 2 устраняет проблему конкуренции. Обычно стоимость (Шаг 1 + Шаг 3) незначительна по сравнению с Шагом 2 (еще более пренебрежимо мала, если Шаг 2 выполняется последовательно). Это хорошо работает, если обработка является узким местом.

Надеюсь это поможет.

person Mahmoud Ben Hassine    schedule 28.02.2019
comment
Спасибо за предложение @Mahmouc. Я уже рассмотрел технику удаленного разбиения. Тем не менее, пакетное задание занимало 1 час с 15 потоками, и мой процессор загружен на 100%. Я очень ценю ваше предложение по разделению данных, но это не то, что я сейчас ищу. Я планирую использовать механизм блокировки базы данных - person Tech Guy; 01.03.2019