Какая связь между настройкой уровня изоляции в коде приложения (например, @Transactional) и настройкой уровня изоляции на сервере базы данных? - PullRequest
1 голос
/ 06 мая 2020

Мне очень трудно собрать всю информацию по кусочкам. Так что позвольте мне объяснить как можно подробнее.

Работая над приложением spring-jdb c, spring-transaction, MySQL db, я хочу иметь возможность блокировать строки данных из таблицы при выборе для обновления. Причина в том, что у меня есть бизнес-логи c, который преобразует данные на разных этапах. Таким образом, если экземпляр приложения (на сервере 1) получает запись для обработки, то ее не должен забирать другой экземпляр. Это несколько похоже на этот вопрос . Но ответ неприемлем для меня по тем же причинам, что и OP для этого вопроса.

Итак, после тщательного изучения ссылки на пружинную транзакцию, сделайте c и узнав, как настроить управление транзакциями с помощью jdbcTemplate, мое приложение Spring настроено следующим образом (только соответствующая часть):

applicationContext. xml

...
...
<context:annotation-config />
...
<context:component-scan base-package="org.foo.bar"/>
...
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
...
<bean id="dataSource"
      class="org.apache.commons.dbcp2.BasicDataSource"  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://dbserver:3306" />
    <property name="username" value="foo" />
    <property name="password" value="baz" />
    <property name="initialSize" value="10" />
    <property name="maxTotal" value="20"/>
</bean>

Класс обслуживания с целевым методом: TransactionSvcImpl. java

public class TransactionSvcImpl implements TransactionSvc {
    @Autowired
    DatabaseAccessService dbAccessService;

    @Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
    @Override
    public List<Foo> getFooForUpdate() {
        // Below call executes a SQL like - "SELECT * FROM some_t WHERE id = 1 FOR UPDATE"
        List<foo> foos = dbAccessService.getSomeRecord();
        dbAccessService.updateTheRecord(foos, Status.PROCESSING);
        return foos;
    }
}

Теперь я проверил, как уровень изоляции работает в MySQL, следуя тому, что описано в этом ответе . Однако для того, чтобы «выбор для обновления» работал, мне пришлось изменить следующие свойства на самом сервере базы данных:

SET tx_isolation = 'SERIALIZABLE';
SET AUTOCOMMIT=0;

Без изменения этих настроек я не смог заставить SELECT ... FOR UPDATE работать.

После того, как я проверил его вручную, я захотел посмотреть, влияет ли это на выполнение кода моего приложения. Итак, пока я поддерживал один из сеансов командной строки mysql активным, я запустил свое приложение и поставил точку останова в строке dbAccessService.updateTheRecord();. Затем в другом сеансе я попытался выбрать эту строку (не select for update, просто простой select). Теперь я мог видеть, что строка была заблокирована из-за предыдущей транзакции, запущенной моим кодом приложения.

Вопросы:

  1. Для достижения блокировки строки (SELECT ... FOR UPDATE поведение) через Spring-transaction мне нужно изменить настройки на сервере базы данных? Например, уровень изоляции и автоматическая фиксация?
  2. Допустим, сервер MySQL работает по умолчанию (ISOLATION_LEVEL = REPEATABLE-READS, AUTOCOMMIT = 1). Теперь это все еще будет работать? Это означает, что если я настрою транзакцию весной для целевого метода, как я сделал выше, будет ли строка, читаемая одним экземпляром приложения, оставаться заблокированной, так что любой другой экземпляр, пытающийся прочитать эту строку, будет заблокирован?
  3. Как влияет ли конфигурация, которую мы добавляем в аннотацию @Transactional, на базу данных (или на нее)? Я знаю, что мы можем установить характеристики транзакции для разных областей, как описано в таблице 13.9 документации MySQL здесь . В основном ниже мое воображение или предположение о том, как все это работает. Это правильно?

Как все это работает?

  • Класс / метод, аннотированный с помощью @Transactional, приведет к созданию прокси-объекта, чтобы все транзакционные данные были добавлено вместе с businiess logi c реализовано пользователем.
  • Установлено соединение с базой данных и создан сеанс с базой данных.
  • Для этого сеанса на основе конфигурации, предоставленной в @Transactional, установлен соответствующий уровень изоляции (не знаю, как это повлияет на автоматическую фиксацию)
  • После завершения транзакции сеанс закрывается. Любые новые созданные сеансы будут соответственно устанавливать характеристики изоляции.

  • Есть ли лучший способ достичь желаемой SELECT ... FOR UPDATE функциональности с помощью spring-jdb c, spring-tx? Я мог бы, вероятно, ввести свой собственный механизм блокировки, например, ввести новый логический столбец, например selected_for_processing, и вместо выбора обновить записи, которые нужно выбрать, установив столбец selected_for_processing. На следующем этапе я бы выбрал на основе этого флага и других критериев статуса. Таким образом, запись будет обработана только один раз. Но я хочу знать, есть ли традиционный способ или способ конфигурации? Я не могу использовать JPA или любые библиотеки spring-data-x. Библиотеки spring-data- работают с springframework 5.x. x или выше, но я застрял с springframework 4.2.3, следовательно, spring-jdb c, spring-tx.

1 Ответ

1 голос
/ 06 мая 2020

Главное, что имеет значение, - это автоматическая фиксация. Обычно автоматическая фиксация включена (каждый запрос представляет собой отдельную транзакцию), и для использования транзакций вам необходимо использовать BEGIN или START TRANSACTION в своем коде, делать то, что вам нужно сделать в транзакции, а затем явно вызывать COMMIT.

1) Настройки на сервере для transaction_isolation изменят значения по умолчанию на сервере. Но вы можете изменить их в локальном сеансе непосредственно перед началом транзакции, не изменяя значение по умолчанию для всего сервера.

2) Да, tx_isolation может отличаться в разных сеансах.

3) Не уверен Я могу ответить на этот вопрос полностью, поскольку, похоже, это специфика Spring c, а я не владею этим свободно. В целом, однако, Spring не имеет реального способа обеспечить или реализовать транзакционность и блокировку на уровне базы данных без явной блокировки целых таблиц, поэтому в конечном итоге все, что он может делать, - это использовать базовые функции базы данных и пытаться быть умнее, чем базовая база данных неизбежно приходит с огромная потеря производительности и параллелизма.

...