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

В этой короткой статье мы рассмотрим, что такое транзакции базы данных, почему вы захотите их использовать и как мы можем использовать транзакции базы данных в Laravel. Без дальнейших церемоний, давайте погрузимся прямо в это!

Проблема

Я работал над классом, позволяющим мне импортировать данные из нескольких (но связанных CSV-файлов) и объединять эти данные для создания набора взаимосвязанных моделей в базе данных.

При разборе около пяти разных файлов CSV мне пришлось создать около девяти разных наборов моделей, которые многократно зависят друг от друга для каждой строки в разных файлах CSV.

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

Это не было проблемой на моем локальном компьютере, потому что я мог просто выполнить php artisan migrate:fresh, но если он не работал на сервере (даже на промежуточном сервере), это была серьезная катастрофа. Представьте, что если в CSV-файлах более 1000 строк, а код дает сбой на полпути к 589-й строке, как нам вручную начать откат записей?

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

Мое решение грубой силы

Я вернулся к чертежной доске, и моей первой мыслью было создать отдельную таблицу track_parses, которая отслеживает все попытки импорта csv. Каждая строка будет представлять сеанс синтаксического анализа, и в ней будет храниться сериализованный объект JSON, который будет отслеживать все записи, которые я успешно создал для каждого сеанса синтаксического анализа.

С помощью этого метода я буду создавать объект track_parses в начале каждого сеанса синтаксического анализа и обновлять объект по мере создания любых записей. Я помещу весь код в блок try-catch, и если код не сработает на полпути, я могу просто пройтись по объекту и удалить все записи, которые я создал, до того, как функция выйдет из строя.
Когда функция завершается успешно, я сериализую объект в JSON и сохраняю его в таблице track_parses, и я могу получить запись обо всех записях, созданных каждым сеансом синтаксического анализа, и по-прежнему откатывать любой сеанс синтаксического анализа, если захочу. Это дало бы мне атомарный алгоритм синтаксического анализа.

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

Сделки на помощь!!!

Давайте немного поговорим о том, что такое транзакции базы данных. Транзакция базы данных — это набор операций или запросов, которые вы делаете к базе данных, которые она объединяет и обрабатывает как один контейнер работы. Это означает, что вы можете отправить кучу запросов, и база данных будет обрабатывать все эти запросы как одно действие.

Затем вы можете rollbackвыполнить это действие — и это отменит все запросы в этом контейнере. Вы также можете commitэто действие — тогда все эти действия будут сохранены в хранилище. Я понимаю транзакции как сохранение всех ваших изменений в памяти, а затем их фактическую запись на диск, когда вы фиксируете изменения, или удаление их из памяти, когда вы откатываете изменения.

Транзакции базы данных доступны в большинстве баз данных SQL, хотя они различаются по надежности, надежности и способу реализации. Однако большие ребята — это MySQL, PostgreSQL, SQLite, Oracle и SQL Server, поддерживающие транзакции, поэтому у вас не должно возникнуть проблем с их реализацией с вашей любимой базой данных SQL.

С этим объяснением вы, должно быть, видели, как транзакции помогли мне сэкономить много кода, все, что мне нужно было сделать, это вписать всю мою логику синтаксического анализа в транзакцию базы данных, обернуть все в блок try-catch, а затем зафиксировать транзакцию, если код работает или откатывает транзакцию в блоке catch.

Хотя это не позволяло вести учет добавленных записей для каждого сеанса синтаксического анализа или откатываться после успешного импорта, это был выгодный компромисс, поскольку они не были обязательными функциями, и это сэкономило мне время для работы над другими особенности и прочее. Кроме того, обратите внимание, что транзакции не мешали мне вести записи, если мне это было нужно — это не было ситуацией «или-или», я также мог добавить объект track_parses в дополнение к транзакциям базы данных.

Транзакции в Laravel

Использование транзакций базы данных в Laravel чрезвычайно просто. Для использования требуется очень мало строк кода. Существует два способа использования транзакций:

Закрытия

Laravel предоставляет замыкание на фасаде DB, в которое вы можете обернуть весь свой код. Вот как выглядит код:

Как вы можете видеть здесь, мы можем обернуть весь наш код в замыкание метода транзакции, и Laravel автоматически обработает транзакцию для нас.

Этот метод хорош для таких простых запросов, но лично мне не нравится использовать этот метод для более сложного кода по одной простой причине — замыкание имеет другую область видимости. Это означает, что я должен присоединить зависимости, необходимые замыканию, с помощью конструкции use(), и это требует много работы по мере роста зависимостей.

Методы транзакций — beginTransaction(), commit(), rollBack().

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

Вот как мы используем эти методы на том же примере:

Как вы можете видеть здесь, мы получаем больше контроля над тем, когда мы хотим выполнить откат. Нам также не нужно использовать какие-либо конструкции use() для всех переменных, которые мы используем.

Примечание. Обратите внимание, что это очень простой фрагмент кода, показывающий транзакции базы данных. В реальном приложении мы будем действовать с большей осторожностью, например проверять и очищать входные данные, следить за тем, чтобы в нашем массиве $fillable не было ненужных полей и т. д.

Предостережение для пользователей MySQL

Если вы используете MySQL и попробовали этот код, и он не сработал, возможно, это связано с тем, что ваша база данных использует движок MyISAM. MySQL в основном имеет два механизма, которые он использует для управления своими таблицами: MyISAM и InnoDB. InnoDB является современным, более быстрым, чем MyISAM, и поддерживает транзакции и внешние ключи. Вообще говоря, это лучший вариант, чем MyISAM. Вы можете проверить эту статью для простого сравнения обоих двигателей.

Исходя из этого, я ожидаю, что базы данных MySQL будут просто использовать InnoDB в качестве механизма по умолчанию для таблиц, но, к сожалению, это не всегда так. При создании миграции вы можете указать механизм, который хотите использовать для таблицы, в файле миграции. Это будет выглядеть так:

Вы также можете автоматически изменить этот параметр для всех будущих таблиц, обновив значение механизма в файле конфигурации базы данных в файле config/database.php. После этого ваш файл конфигурации будет выглядеть следующим образом:

Если вы понимаете, что уже создали свои таблицы базы данных, и они используют механизм MyISAM (и, как и я, вы работаете с базой данных с более чем 50 таблицами), вы можете легко создать миграцию для автоматической преобразовать все таблицы в вашей базе данных в предпочитаемый вами движок. Миграция будет выглядеть так:

Заключение

Я знаю, что сказал, что статья короткая в первом абзаце, я честно думал, что это будет, когда я начал писать — извините. В любом случае, сделки потрясающие. Это одна из тех замечательных приправ к еде, о которой вы даже не подозревали. Их можно применять во многих сценариях, чтобы ваш код был атомарным.