В данной статье речь пойдёт об использовании механизма транзакций при работе с БД MySQL в Битриксе.
Первым делом стоит сразу отметить, что поддержка транзакций (ровно как и ограничений целостности) в MySQL зависит от выбранного Вами движка БД. Движков этих существует некое множество, но широкое распространение получили два из них: MyISAM и InnoDB. На бескрайних просторах Гугла можно найти бесчисленное количество холиваров на тему, какой же движок лучше и для каких целей. Однако, для нас важно другое - транзакции не поддерживаются в MyISAM, транзакции поддерживаются в InnoDB. Поэтому, если у Вас в качестве движка стоит MyISAM и Вы хотите использовать транзакции, Вам придётся сменить движок с помощью команды ALTER.
Отдельно стоит заметить, что во избежание deadlock'ов в БД, работающей на InnoDB, необходимо отключить устойчивое соединение. В Битриксе это делается это по средствам настройки константы DBPersistent в файле dbconn.php.
Теперь для использования транзакций вместе с API Битрикса остаётся только одна проблема: InnoDB не поддерживает вложенных транзакций (такая поддержка есть на сколько мне известно в Oracle). То есть в результатом запроса
CREATE TABLE a
(
data varchar(255)
) ENGINE=InnoDB;
BEGIN;
INSERT INTO `a` (`data`) VALUES ('a');
BEGIN;
ROLLBACK;
ROLLBACK;
SELECT * FROM `a`;
будет строка с полем `data` равным "a". Это происходит потому, что ввиду отсутствия поддержки механизма вложенных транзакций второй BEGIN автоматически делает COMMIT для предыдущего, тем самым закрывая в случае наличия текущую транзакцию и открывая новую. Ввиду выше описанного возникает в целом неразрешимая проблема. Если в своих модулях (например в модуле iblock) пацанчики из Битрикса начнут использовать транзакции, использование транзакций на уровне API (пример ниже) будет бессмысленно. Однако, анализ исходников Битрикса (grep -ri ) показал, что транзакции в модулях Битрикса не текущий момент не используются.
Перейдём к примеру. Описывать его не буду, так код в целом самодокументирован.
<?php
/**
* Пример использования транзакций
*
* Суть примера заключается в создании двух элементов одного инфоблока.
* В случае, если хотя бы один элемент не может быть создан, происходит
* откат всех изменений.
*/
// Подключение Битрикса
require( $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php' );
CModule::IncludeModule( 'iblock' );
// Настройки
define( 'MY_IBLOCK_ID', 1 );
// Инициализация переменных
$bSuccess = true;
$oIBlockElement = new CIBlockElement();
// Запуск транзакции
$DB->StartTransaction();
try
{
// Добавление первого элемента
$iElementId = $oIBlockElement->Add( array(
'IBLOCK_ID' => MY_IBLOCK_ID,
'NAME' => 'ONE',
) );
if( false === $iElementId )
throw new Exception( $oIBlockElement->LAST_ERROR );
// Добавление второго элемента
$iElementId = $oIBlockElement->Add( array(
'IBLOCK_ID' => MY_IBLOCK_ID,
'NAME' => 'TWO',
) );
if( false === $iElementId )
throw new Exception( $oIBlockElement->LAST_ERROR);
// Сохранение данных
$DB->Commit();
}
// Обработка ошибок
catch( Exception $ex )
{
// Откат изменений
$DB->Rollback();
// Сброс флага успеха
$bSuccess = false;
echo 'An error occurred: ' . $ex->getMessage();
}
Область применения можете додумать сами - это по сути любой момент, где требуется соблюсти атомарность записи: создание заказа, шаги мастера (чисто для примера) и так далее.
На последок, хочу ещё раз подчеркнуть, что так как лично у меня нет гарантий, что в модули Битрикса рано или поздно не начнут использование транзакций, полагаться на транзакции с InnoDB Вам следует на свой страх и риск. В случае, если же Ваша БД поддерживает вложенные транзакции - волноваться не о чем
Отдельно стоит заметить, что во избежание deadlock'ов в БД, работающей на InnoDB, необходимо отключить устойчивое соединение. В Битриксе это делается это по средствам настройки константы DBPersistent в файле dbconn.php.
Объясните пожалуйста, как связано постоянное подключение к БД с блокировками? И какими именно какой тип блокировки?
На счёт deadlock'ов - прошу прощения, походу я погорячился. Просто от многих знакомых приходилось слышать рекомендации не использовать устойчивые соединения с транзакционными БД, ибо это тянет за собой кучу проблем, таких как зависшие процессы в БД. В качестве примера сейчас могу привести разве что этот пост.
Область применения можете додумать сами - это по сути любой момент, где требуется соблюсти атомарность записи: создание заказа, шаги мастера (чисто для примера) и так далее.
На обработчики событий смотрели? Могут подложить сюрприз.
Итак, мы создали заказ (элемент), а на основе его пробуем сделать еще что-то. 2-я запись в БД не получается и идет откат. Если на добавление первой записи было событие, то оно сработает (а почему бы и нет?). Не все так просто.
То же самое с "длительными" операциями.
Сам я тоже долго размышлял на эту тему и пришел к выводу, что если работая чистыми методами АПИ у меня "не проходят" (отпадают и т..д) запросы к БД, то это уже проблема не локального, а всего проекта целиком.
Такой механизм стоит использовать аккуратно, когда вы точно знаете зачем вам.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».