我有一个由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;
SELECT ... FOR UPDATE
是事务安全的。我会分析是否可以用它替换我的逻辑(MySQL关于它的文档几乎不能更混乱)。您应该将您的评论扩展成一个合适的答案。声明LOCK TABLES
无法与事务很好地配合,并提供解决方法非常有用的信息。 - Álvaro González