Тупик базы данных в SELECT FOR UPDATE

Я периодически получаю тупик в своем приложении. В моем приложении есть 1 таблица, например EMPLOYEE (ID (PK), NAME, SAL), и есть 2 сеанса.

Сессия 1:

SELECT ID, NAME, SAL FROM EMPLOYEE WHERE SAL = (SELECT MIN(SAL) FROM 
EMPLOYEE) FOR UPDATE
Let say the query return EMPLOYEE ROW having ID=2
then application does some processing like rs.updateInt(ID_SAL, 10);

Сессия 2: (для другой бизнес-логики)

SELECT ID, NAME, SAL FROM EMPLOYEE WHERE ID=2 FOR UPDATE.

Итак, в приложении оба сеанса пытаются обновить одну и ту же строку (в примере строка с ID = 2). Такая ситуация ожидается, и поэтому я подумал, что SELECT .. FOR UPDATE поможет.

Я делаю что-то неправильно? Я предполагаю, что SELECT FOR UPDATE заблокирует строку, и когда другой сеанс попытается обновить ту же строку, он будет ждать, пока сеанс 1 не завершит выполнение.


person Danny Zen    schedule 11.10.2013    source источник


Ответы (1)


Я предполагаю, что SELECT FOR UPDATE заблокирует строку, и когда другой сеанс попытается обновить ту же строку, он будет ждать, пока сеанс 1 не завершит выполнение.

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

Процесс 1 блокирует строку с ID=2, обновляет ее и переходит к следующей записи с ID=1 (но сеанс и транзакция все еще активны) Процесс 2 уже заблокировал строку с ID=1 и собирается заблокировать строку с ID=2 (но сеанс и транзакция все еще активны)

Таким образом, процесс 1 ожидает записи с идентификатором = 1 и удерживает запись с идентификатором = 2.

Процесс 2 ожидает записи ID=2 и удерживает запись ID=1

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

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

person Nicolai    schedule 11.10.2013
comment
Спасибо Николай за быстрый ответ. Да, вы правы в объяснении. В обоих сеансах я фиксирую/откатываю транзакцию после завершения обработки, т.е. rs.updateRow(). В сеансе 1 мое приложение обрабатывает только 1 строку (примечание: запрос сеанса 1 может возвращать несколько строк, но я обрабатываю только 1 строку). И запрос сеанса 2 всегда будет возвращать 1 строку. - person Danny Zen; 11.10.2013
comment
Самое главное — это бесплатная (завершить транзакцию или закрыть сеанс) запись или большое количество записей перед переключением на другие записи в обоих процессах. - person Nicolai; 11.10.2013