#mysql #stored-procedures #transactions #mysql-5.5
#mysql #хранимые процедуры #транзакции #mysql-5.5
Вопрос:
Краткая версия: Если есть запущенная транзакция, которая, возможно, будет откатана, как можно что-то записать в таблицу журналов? (Откат транзакции также приведет к откату записей журнала, что является проблемой здесь.)
Длинная версия:
У меня есть хранимая процедура, давайте вызовем ее, sp_A
которая вызывает другие хранимые процедуры, давайте вызовем их sp_B
и sp_C
. Для обеспечения согласованности внутри XA TRANSATCION
запускается sp_A
. Что я на самом деле хочу сделать, так это войти в систему, если выполнение sp_B
или sp_C
завершается неудачей, и если да, то какие аргументы были переданы сбойной хранимой процедуре. Но поскольку sp_B
и sp_C
выполняют некоторые вставки в некоторых таблицах, мне нужно выполнить, XA ROLLBACK
если sp_B
или sp_C
произойдет сбой.
Итак, мой вопрос таков: как я могу войти в таблицу, находясь внутри транзакции (или как выполнить инструкцию insert вне области транзакции)?
Чтобы дать вам небольшой пример того, что я хочу сделать (я упростил код и переименовал большинство параметров / переменных, используемых для решения проблемы, я бы никогда не мечтал использовать такие имена параметров, как a
, b
или c
;-)):
Хранимая процедура sp_A
выглядит следующим образом:
CREATE PROCEDURE `sp_A`
(
IN a INT,
IN b VARCHAR(64),
OUT c INT
)
sp_label:BEGIN
DECLARE l_errorMessage TEXT;
DECLARE l_spSuccess INT;
DECLARE l_errorOccured INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
SET l_errorOccured = 1;
END;
XA START 'sp_A';
CALL sp_B(a, b, l_spSuccess);
IF l_errorOccured OR (l_spSuccessResult < 0) THEN
XA END 'sp_A';
XA ROLLBACK 'sp_A';
SET c = -1;
SET l_errorMessage = CONCAT('CALL sp_B(', a, ', ', b, ', l_spSuccess);');
CALL sp_Log(l_errorMessage);
LEAVE sp_label;
END IF;
CALL sp_C(a, b, l_spSuccess);
IF l_errorOccured OR (l_spSuccessResult < 0) THEN
XA END 'sp_A';
XA ROLLBACK 'sp_A';
SET c = -2;
SET l_errorMessage = CONCAT('CALL sp_C(', a, ', ', b, ', l_spSuccess);');
CALL sp_Log(l_errorMessage);
LEAVE sp_label;
END IF;
XA END 'sp_A';
XA COMMIT 'sp_A';
SET c = 1;
END;
Хранимая процедура sp_B
выглядит следующим образом ( sp_C
выглядит аналогично):
CREATE PROCEDURE `sp_B`
(
IN a INT,
IN b VARCHAR(64),
OUT c INT
)
sp_label:BEGIN
DECLARE l_errorMessage TEXT;
DECLARE l_spSuccess INT;
DECLARE l_errorOccured INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
SET l_errorOccured = 1;
END;
INSERT INTO `someTable` (`a`, `b`) VALUES (a, b);
IF l_errorOccured THEN
SET c = -1;
SET l_errorMessage = CONCAT('INSERT INTO `someTable` (`a`, `b`) VALUES (', a, ', ', b, ');');
CALL sp_Log(l_errorMessage);
LEAVE sp_label;
END IF;
SET c = 1;
END;
sp_Log
просто вставляет данные в таблицу, которую я хочу использовать как свою logging table
.
Таким образом, на самом деле ведение журнала внутри sp_A
работает просто отлично (потому что вызов sp_Log
находится вне транзакции), но ведение журнала внутри sp_B
или sp_C
не будет работать, потому что при откате транзакции, запущенной в sp_A
, вставки, выполненные вызовом sp_Log
внутри sp_B
или sp_C
, конечно, отменяются.
(Я упростил пример в sp_B
и sp_C
. Происходит нечто большее, чем просто одна простая инструкция insert, и просто для большего удовольствия внутри sp_B
and sp_C
также запускаются и фиксируются / откатываются транзакции.)
Я был бы признателен за любые подсказки, которые вы могли бы мне дать. Спасибо!
Комментарии:
1. Кто бы ни проголосовал против… Пожалуйста, дайте мне подробную информацию, почему вы это сделали, чтобы я мог улучшить вопрос…
2. Я попытался включить команды журнала в другую транзакцию (мой «main» эквивалентен вашему «sp_a»). Что-то вроде: ЕСЛИ
_rollback
, ТО XA ЗАВЕРШИТЕ «main»; XA ПОДГОТОВЬТЕ «main»; XA ВЫПОЛНИТЕ ОТКАТ «main»; XA ЗАПУСТИТЕ «log»; ВСТАВЬТЕ В logtable (message
) ЗНАЧЕНИЯ (‘Rolled back’); XA ЗАВЕРШИТЕ «log»; XA ЗАФИКСИРУЙТЕ «log» ОДНУ ФАЗУ; ОСТАВЬТЕ this_procedure; ЗАВЕРШИТЕ IF; К сожалению, я сталкиваюсь с дополнительными, не связанными проблемами, поэтому я не могу объявить свое решение рабочим.
Ответ №1:
Кажется, что одним из жизнеспособных способов решения этой проблемы является использование табличного движка, который не поддерживает транзакции. В этом случае изменение табличного механизма таблиц журнала с InnoDB
на MyISAM
решило проблему.