Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому міграцію з кількома ALTER TABLE не можна відкотити транзакцією в MySQL?

У MySQL оператори визначення схеми (DDL) виконують неявний коміт: перед виконанням вони комітять поточну транзакцію, і самі в транзакцію не потрапляють.

START TRANSACTION;
INSERT INTO settings (name) VALUES ('a');
ALTER TABLE settings ADD COLUMN value TEXT;   -- INSERT уже закомічено
ROLLBACK;                                     -- відкочувати нічого

Що викликає неявний коміт (основне):

  • CREATE, ALTER, DROP, RENAME, TRUNCATE TABLE, створення й видалення індексів, подань, процедур;
  • LOCK TABLES і UNLOCK TABLES (якщо таблиці були заблоковані);
  • START TRANSACTION / BEGIN - комітять попередню незавершену транзакцію;
  • адміністративні: ANALYZE TABLE, OPTIMIZE TABLE, CREATE USER, GRANT тощо.

CREATE TEMPORARY TABLE неявного коміту не робить.

Що це означає для Laravel-міграцій:

public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('phone')->nullable();
    });

    Schema::table('users', function (Blueprint $table) {
        $table->string('phone')->unique()->change();   // впаде на дублікатах
    });
}

Якщо другий крок падає, перший уже застосований. Міграцію не позначено виконаною, тож повторний запуск упаде на «колонка вже існує». Доведеться вручну прибрати наполовину застосовані зміни.

Атомарний DDL у MySQL 8 означає інше: окремий оператор DDL або виконується повністю, або не виконується (не лишає «напівстворених» таблиць після збою). Але кілька операторів у транзакцію все одно не об'єднати.

PostgreSQL тут відрізняється: DDL у ньому транзакційний, і Laravel обгортає міграцію в транзакцію ($withinTransaction = true), тож упала - відкотилася повністю.

Практичні висновки для MySQL:

  • одна міграція - одна логічна зміна;
  • ризиковану зміну (унікальний індекс на дані з можливими дублікатами) - окремою міграцією, після перевірки й очищення даних;
  • метод down() має вміти відкотити зміни, навіть якщо частину з них не застосовано (if (Schema::hasColumn(...))).

Докладніше в документації: Оператори з неявним комітом

1

Схожі питання