【问题标题】:Rollback transactions with LOCK TABLES使用 LOCK TABLES 回滚事务
【发布时间】:2011-06-18 11:18:50
【问题描述】:

我有一个 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;

【问题讨论】:

  • 为什么需要锁定?真正的问题是什么?
  • 我需要锁定以确保只有一个进程能够使用当前年份的序列号,并且序列中不会留下任何间隙。真正的问题是,当您尝试使用不支持事务的功能(例如表锁定)时,MySQL 会自动提交未经验证的数据集,从而超出了使用事务的全部意义。
  • 你不能使用 SELECT .... FOR UPDATE; ?在交易中工作正常,完全没有问题。对序列使用单个记录并每次更新此记录。
  • @Frank Heikens:我做了一些快速测试,SELECT ... FOR UPDATE 是交易安全的。我将分析我是否可以用它替换我的逻辑(关于它的 MySQL 文档几乎不会更令人困惑)。您应该将您的评论扩展为正确的答案。说明 LOCK TABLES 无法修复以很好地处理事务并提供解决方法是非常有用的信息。
  • @Frank Heikens - 我创建了一个实际答案,因此可以将问题标记为已回答。

标签: mysql transactions locking innodb


【解决方案1】:

显然,LOCK TABLES 无法修复以很好地处理事务。一种解决方法是将其替换为SELECT .... FOR UPDATE。你不需要任何特殊的语法(你可以使用常规的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语句来获取你真正需要的数据;-)

(此信息由 Frank Heikens 提供。)

【讨论】:

  • 请注意 SELECT ... FOR UPDATE 仍然允许其他数据库会话从表中读取。我希望这可以在某人依赖此功能之前对其有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多