Откат транзакций с помощью LOCK TABLES - PullRequest
5 голосов
/ 31 января 2011

У меня есть приложение на основе PHP / 5.2, которое использует транзакции под MySQL / 5.1, поэтому оно может откатывать несколько вставок, если выполняется условие ошибки. У меня есть разные многоразовые функции для вставки разных типов предметов. Пока все хорошо.

Теперь мне нужно использовать блокировку таблицы для некоторых вставок. Как следует из официального руководства, я использую SET autocommit=0 вместо START TRANSACTION, поэтому LOCK TABLES не выдает неявный коммит. И, как задокументировано, разблокировка таблиц неявно фиксирует любую активную транзакцию:

И здесь кроется проблема: если я просто избегаю UNLOCK TABLES, случается, что второй вызов LOCK TABLES фиксирует ожидающие изменения!

Похоже, что единственный способ - выполнить все необходимые LOCK TABLES в одном выражении. Это главный кошмар.

Есть ли у этой проблемы разумное решение?

Вот небольшой тестовый скрипт:

DROP TABLE IF EXISTS test;

CREATE TABLE test (
    test_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    random_number INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (test_id)
)
COLLATE='utf8_spanish_ci'
ENGINE=InnoDB;


-- No table locking: everything's fine
START TRANSACTION;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;



-- Table locking: everything's fine if I avoid START TRANSACTION
SET autocommit=0;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;



-- Table locking: I cannot nest LOCK/UNLOCK blocks
SET autocommit=0;
LOCK TABLES test WRITE;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
UNLOCK TABLES; -- Implicit commit
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;


-- Table locking: I cannot chain LOCK calls ether
SET autocommit=0;
LOCK TABLES test WRITE;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
-- UNLOCK TABLES;
LOCK TABLES test WRITE; -- Implicit commit
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
-- UNLOCK TABLES;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;

1 Ответ

4 голосов
/ 09 февраля 2011

Очевидно, LOCK TABLES нельзя исправить, чтобы хорошо играть с транзакциями. Обходной путь должен заменить это на ВЫБРАТЬ .... ДЛЯ ОБНОВЛЕНИЯ . Вам не нужен специальный синтаксис (вы можете использовать обычный START TRANSACTION), и он работает как положено:

START TRANSACTION;
SELECT COUNT(*) FROM foo FOR UPDATE; -- Lock issued
INSERT INTO foo (foo_name) VALUES ('John');
SELECT COUNT(*) FROM bar FOR UPDATE; -- Lock issued, no side effects
ROLLBACK; -- Rollback works as expected

Обратите внимание, что COUNT(*) является лишь примером, обычно вы можете использовать оператор SELECT для получения данных, которые вам действительно нужны; -)

(эту информацию предоставил Фрэнк Хейкенс.)

...