2013-12-16 2 views
4

У меня есть два параллельных транзакций исполняющих этот бит кода (упрощено для целей иллюстрации):Нужна помощь, чтобы понять поведение SELECT ... FOR UPDATE вызывает тупиковый

@Transactional 
public void deleteAccounts() { 
    List<User> users = em.createQuery("select u from User", User.class) 
         .setLockMode(LockModeType.PESSIMISTIC_WRITE) 
         .getResultList(); 
    for (User user : users) { 
     em.remove(user); 
    } 
} 

Я понимаю, что одна из сделок , скажем, транзакция A, должна сначала выполнить SELECT, заблокировать все строки, в которых она нуждается, а затем продолжить с помощью DELETE, а другая транзакция должна дождаться фиксации A до выполнения SELECT. Однако этот код блокируется. Где я ошибаюсь?

+0

Какое сообщение об исключении для тупика? –

+0

Исключение [EclipseLink-4002] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.DatabaseException Внутреннее исключение: java.sql.SQLException: ORA-00060: обнаружен тупик при ожидании ресурса Код ошибки: 60 –

+0

Есть ли у вас отношения между пользователями? –

ответ

0

Когда вы используете PESSIMISTIC_WRITE JPA, обычно передавайте его в SELECT FOR UPDATE, это делает блокировку в базе данных, не обязательно для строки, которая зависит от базы данных и как вы настраиваете блокировку, по умолчанию блокировка осуществляется по страницам или блок не для строки, поэтому проверьте документацию в базе данных, чтобы подтвердить, как ваша база данных делает блокировку, также вы можете ее изменить, чтобы вы могли применить блокировку для строки. Когда вы вызываете метод deleteAccounts, он запускает новую транзакцию, и блокировка будет активна до тех пор, пока транзакция не совершит (или откат) в этом случае, когда метод закончен, если другая транзакция хочет получить тот же замок, она не может и я подумайте, что именно поэтому у вас есть мертвый замок, я предлагаю вам попробовать какой-то механизм, возможно, оптимистичный замок или блокировку сущностью.

Вы можете попробовать данный тайм-аут, чтобы приобретаемой замок так:

em.createQuery("select u from User", User.class) 
.setLockMode(LockModeType.PESSIMISTIC_WRITE) 
.setHint("javax.persistence.lock.timeout", 5000) 
.getResultList(); 

я нашел хороший article что объясняет лучше эту ошибку, она вызывает в базе данных:

Oracle automatically detects deadlocks and resolves them by rolling back one of the transactions/statements involved in the deadlock, thus releasing one set of resources/data locked by that transaction. The session that is rolled back will observe Oracle error: ORA-00060: deadlock detected while waiting for resource. Oracle will also produce detailed information in a trace file under database's UDUMP directory.

Most commonly these deadlocks are caused by the applications that involve multi table updates in the same transaction and multiple applications/transactions are acting on the same table at the same time. These multi-table deadlocks can be avoided by locking tables in same order in all applications/transactions, thus preventing a deadlock condition.

+0

«если другая транзакция хочет получить тот же замок, что и не может» - но тогда будет тайм-аут, а не тупик, правильно? Поскольку эта новая транзакция еще ничего не приобрела, поэтому не должно быть причин для тупика. –

+0

Да, это правда, но вы можете указать тайм-аут 0, это странное поведение, но если вы видите исключение, это база данных, которая бросает тупик, обнаруживая тупик, обнаруженный во время ожидания ресурса, возможно, вы можете указать время ожидания 5 секунд только для контрольная работа. –

+0

Поставщик поставщика настраивает время ожидания блокировки, вы можете указать значение тайм-аута как подсказку, отредактировать свой ответ, чтобы показать пример. –

1

В таблице USER, вероятно, есть много внешних ключей, ссылающихся на нее. Если какой-либо из них не проиндексирован, Oracle блокирует всю дочернюю таблицу, когда удаляет строку из родительской таблицы. Если несколько операторов запускаются одновременно, даже для другого пользователя, те же дочерние таблицы будут заблокированы. Поскольку порядок этих рекурсивных операций нельзя контролировать, возможно, что несколько сеансов будут блокировать одни и те же ресурсы в другом порядке, вызывая тупик.

См. this section в руководстве по понятиям для получения дополнительной информации.

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

--Find un-indexed foreign keys. 
-- 
--Foreign keys. 
select owner, table_name 
from dba_constraints 
where r_constraint_name = 'USER_ID_PK' 
    and r_owner = 'THE_SCHEMA_NAME' 
minus 
--Tables with an index on the relevant column. 
select table_owner, table_name 
from dba_ind_columns 
where column_name = 'USER_ID'; 
+1

Nitpick: В БД может быть несколько пользователей, которым принадлежит ограничение с именем USER_ID_PK, поэтому этот запрос может возвращать ложные срабатывания. Я бы рекомендовал добавить 'и r_owner = ...' к первому подзапросу. –

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