Правильный дизайн, чтобы избежать тупиков Oracle?

Обычный совет, когда речь идет о том, чтобы избежать тупиков, - это всегда блокировать ресурсы в том же порядке. Но как бы вы реализовали это в отношении блокировков строки в очень довольной базе данных 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 говорит, что это связано с плохой конструкцией приложений. Но какой хороший дизайн в этом случае? Мне нужно Выбрать для обновления Все, что планирую обновить? Что если это огромная транзакция, связанная с обновлениями нескольких таблиц? Должен ли я проектировать, чтобы мертвые замки невозможны или просто минимизировать их и принять это Это факт жизни?

8
задан Community 23 May 2017 в 12:19
поделиться