Как создать историю схемы Flyway с уровнем изоляции моментальных снимков?

#sql-server #spring #flyway #transaction-isolation

#sql-сервер #весна #flyway #транзакция-изоляция #sql-server

Вопрос:

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

Это приложение Spring Boot (v2.2.0), и я пытаюсь настроить миграцию с помощью Flyway версии v.7.1.1.

Я сгенерировал существующую схему из базы данных и установил ее как базовую версию V1. Однако при попытке запустить приложение (с базовым уровнем при переносе: true) миграция завершается неудачно из-за исключения:

 Caused by: org.flywaydb.core.internal.sqlscript.FlywaySqlScriptException: 
Migration  failed
-----------------
SQL State  : S0001
Error Code : 3964
Message    : Transaction failed because this DDL statement is not allowed inside a snapshot isolation transaction. Since metadata is not versioned, a metadata change can lead to inconsistency if mixed within snapshot isolation.
Location   :  ()
Line       : 1
Statement  : CREATE TABLE [TestDB].[dbo].[flyway_schema_history] (
    [installed_rank] INT NOT NULL,
    [version] NVARCHAR(50),
    [description] NVARCHAR(200),
    [type] NVARCHAR(20) NOT NULL,
    [script] NVARCHAR(1000) NOT NULL,
    [checksum] INT,
    [installed_by] NVARCHAR(100) NOT NULL,
    [installed_on] DATETIME NOT NULL DEFAULT GETDATE(),
    [execution_time] INT NOT NULL,
    [success] BIT NOT NULL
);
  

Из конфигурации приложения конфигурация источника данных HikariCP настроена на использование:

транзакция-изоляция: TRANSACTION_SQL_SERVER_SNAPSHOT_ISOLATION_LEVEL

Если изоляция транзакции изменена, например, на TRANSACTION_READ_COMMITTED, миграция будет успешной. Я мог бы выполнить все остальные миграции, как и ожидалось. Архитектор программного обеспечения сказал мне, что причина уровня изоляции моментальных снимков заключается в том, что приложение используется для генерации огромных отчетов, что может занять несколько часов, а уровень изоляции моментальных снимков «защищает нас от зависания и блокировок приложений».

Я попытался обойти это с помощью создания обратных вызовов, где я мог бы изменить изоляцию транзакций во время выполнения, в обратных вызовах Java beforeMigrate и afterMigrate, но конфигурация HikariDataSource не может быть изменена во время выполнения.

Может кто-нибудь помочь с некоторыми советами, возможна ли миграция (и создание базовой схемы) с изоляцией моментальных снимков?

Ответ №1:

Я большой поклонник изоляции моментальных снимков в SQL Server, но она не очень подходит в качестве уровня изоляции для всех ваших развертываний.

Вот три основные вещи, которые следует учитывать:

1. Пределы изоляции моментальных снимков в отношении DDL и явных транзакций с несколькими операторами

Изоляция моментальных снимков невероятно полезна, однако SQL Server ограничивает то, для чего вы можете ее использовать, как указано в сообщении об ошибке:

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

Это относится не только к созданию таблицы flyway_schema_history, SQL Server также применит это правило к другим изменениям DDL, которые у вас есть в сценариях миграции.

Дополнительная информация приведена в документации здесь:

SQL Server не поддерживает управление версиями метаданных. По этой причине существуют ограничения на то, какие операции DDL могут выполняться в явной транзакции, которая выполняется при изоляции моментального снимка. Следующие инструкции DDL не разрешены при изоляции моментального снимка после инструкции BEGIN TRANSACTION: ALTER TABLE, CREATE INDEX, CREATE XML INDEX, ALTER INDEX, DROP INDEX, DBCC REINDEX, ALTER PARTITION FUNCTION, ALTER PARTITION SCHEME или любая инструкция DDL common language runtime (CLR).

2. Блокировки изменения схемы требуются для операций DDL независимо от того, что

Кроме того, даже при изоляции моментальных снимков для изменений DDL потребуется блокировка изменения схемы высокого уровня (SCH-M), которая требует эксклюзивного доступа к таблице. Подробнее об этом в той же статье:

Компонент SQL Server Database Engine использует блокировки модификации схемы (Sch-M) во время операций с языком определения табличных данных (DDL), таких как добавление столбца или удаление таблицы. Во время его хранения блокировка Sch-M предотвращает одновременный доступ к таблице. Это означает, что блокировка Sch-M блокирует все внешние операции до тех пор, пока блокировка не будет снята.

Некоторые операции языка обработки данных (DML), такие как усечение таблицы, используют блокировки Sch-M для предотвращения доступа к затронутым таблицам с помощью параллельных операций.

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

3. Если вы используете Snapshot Iso для изменений данных, вы должны быть готовы к обработке конфликтов обновлений

Снимок может использоваться для обновления данных в операторах DML, но это добавляет сложности, для которых вам нужно будет добавить обработку ошибок. Конфликты обновлений могут возникать, если данные в таблице могут быть изменены двумя процессами одновременно:

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

Где может помочь изоляция моментальных снимков

Из того, что вы сказали, я думаю, что для приложения / отчетов имеет смысл продолжать использовать изоляцию моментальных снимков, но для вас запускать свои развертывания в Flyway с уровня изоляции по умолчанию для чтения.

Пока приложение / отчеты используют изоляцию моментальных снимков, это означает, что они не будут заблокированы никакими вставками / обновлениями / удалениями, которые вы выполняете из своих развертываний flyway.

Любой DDL, который вы изменяете, изменяя схему, может потенциально блокировать отчеты. Однако использование изоляции моментальных снимков в развертываниях не изменит этого для вас — вам в любом случае нужны блокировки SCH-M.

Кендра

Комментарии:

1. Привет, Кендра, спасибо за ваш ответ. Это помогло мне лучше понять проблему. Однако, основываясь на вашем ответе, у меня есть несколько предположений, в правильности которых я не уверен. 1. Это определенно означает, что я не могу использовать уровень iso моментального снимка для развертываний Flyway из-за исключения SQL Server выше? 2. Основываясь на вашем предложении, я мог бы выбрать вариант использования уровня изоляции READ_COMMITED, в то время как в приложении, где это имеет смысл, передавать другой уровень изоляции в аннотации @Transactional? Запутанная часть для меня, что происходит, когда установлены разные уровни ISO, одна ит-СУБД и другая в Hikari CP

2. Привет — вы можете использовать МОМЕНТАЛЬНЫЙ СНИМОК, но вам нужно будет включать и выключать его в сценариях миграции для типов транзакций / операторов, для которых SQL Server его разрешает — например: УСТАНОВИТЕ МОМЕНТАЛЬНЫЙ СНИМОК ИЗОЛЯЦИИ ТРАНЗАКЦИЙ; (и ВЫКЛЮЧЕН) Уровень изоляции зависит от каждого сеанса, и вы можете его изменитьна протяжении всего сеанса. СУБД влияет на уровень изоляции по умолчанию. Разные сеансы могут использовать разные уровни изоляции одновременно. Надеюсь, это поможет!

Ответ №2:

Мне удалось обойти уровень изоляции источника данных приложения Hikari, создав отдельный источник данных для миграции Flyway, где для изоляции транзакций установлено значение READ_COMMITED. Миграция пролетного пути выполнена успешно, и конфигурация основного источника данных не затронута (с изоляцией моментальных снимков).

 @Bean
@FlywayDataSource
public DataSource flywayDataSource(@Autowired @Qualifier(value = "primaryDataSource") DataSource primaryDataSource) {
    HikariDataSource hds = (HikariDataSource) primaryDataSource;
    HikariConfig flywayHikariConfig = new HikariConfig();
    hds.copyStateTo(flywayHikariConfig);
    flywayHikariConfig.setTransactionIsolation(IsolationLevel.TRANSACTION_READ_COMMITTED.toString());
    return new HikariDataSource(flywayHikariConfig);
}
  

Я не смог применить аналогичную логику с обратными вызовами Flyway, Java или SQL, где уровень ISO изменен.
Однако я думаю, что это можно сделать по-другому, установив @Transactional(изоляция = Изоляция.READ_COMMITTED) на каком-то компоненте конфигурации Flyway, но я не смог заставить его работать (например, попробовал с помощью FlywayMigrationStrategy).