Обычный совет, когда речь идет о том, чтобы избежать тупиков, - это всегда блокировать ресурсы в том же порядке. Но как бы вы реализовали это в отношении блокировков строки в очень довольной базе данных Oracle?
, чтобы увидеть, что я имею в виду, рассмотрим следующий пример. Очень простой DAO для обработки банковских счетов:
@Component
public class AccountDao {
@Resource
private DataSource dataSource;
public void withdraw(String account, int amount) {
modifyBalance(account, -amount);
}
public void deposit(String account, int amount) {
modifyBalance(account, amount);
}
private void modifyBalance(String account, int amount) {
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
PreparedStatement statement = connection
.prepareStatement("update account set balance = balance + ? where holder = ?");
statement.setInt(1, amount);
statement.setString(2, account);
statement.execute();
}
catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
для выполнения передачи между двумя аккаунтами, есть какой-то тип класса InternalBanktransfer
, который имеет метод передачи:
public void transfer(String from, String to, int amount) {
// start transaction
accountDao.withDraw(from, amount);
accountDao.deposit(to, amount);
// commit transaction
}
обычно это работает нормально. Но скажем, что у нас одновременно у нас есть два человека, инициирующих передачу. Допустим, Энн хочет перенести 100 баксов в Боб в то же время, когда Боб хочет передать 50 в Энн. Итак, в одном потоке Anne звонков Трансфер («Анна», «Боб», 100)
, а в другом Боб звонит Трансфер («Боб», «Анн», 50)
. Этот код восприимчив к мертвым замкам, если заказ выполнения выглядит следующим образом:
T1: accountDao.withDraw("Anne", 100);
T2: accountDao.withDraw("Bob", 50);
T1: accountDao.deposit("Bob", 100);
T2: accountDao.deposit("Anne", 50); // BAM! ORA-00060: deadlock detected while waiting for resource
Я признаю, что я не считал это вообще, прежде чем я начал видеть мертвые замки в реальном приложении. Мой наивный взгляд заключался в том, что изоляция транзакции позаботилась об этом автоматически. Oracle говорит, что это связано с плохой конструкцией приложений. Но какой хороший дизайн в этом случае? Мне нужно Выбрать для обновления
Все, что планирую обновить? Что если это огромная транзакция, связанная с обновлениями нескольких таблиц? Должен ли я проектировать, чтобы мертвые замки невозможны или просто минимизировать их и принять это Это факт жизни?