如何锁定InnoDB表以防止在复制该表时进行更新?

时间:2013-01-09 22:40:35

标签: mysql locking innodb

我想暂时锁定一个表,以防止其他并发进程对其进行更改。原因是这个表将被复制到临时表,更改,然后被复制回来(实际上删除了原始表并重命名了新表)。然后在完成所有这些之后,我想解锁表格并希望在锁定恢复期间尝试任何尝试。

我还需要能够从已锁定的表中读取以构建新表。

这是我尝试过的,但似乎没有效果。

$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT);

$mysqli->autocommit(false)

// create a table to hold parts to keep
$q1 = "CREATE TABLE new_table LIKE my_table;";
$res1 = $mysqli ->query($q1);       

// Lock table to avoid concurrent update issues
$q2 = "LOCK TABLE my_table WRITE;";
$res2 = $mysqli ->query($q2);           

// Insert data to keep into new table   
$q3 = "INSERT INTO new_table SELECT * FROM my_table WHERE some_id IN (SELECT ID FROM table2)";
$res3 = $mysqli ->query($q3);

// drop original table
$q4 = "DROP TABLE my_table;";
$res4 = $mysqli ->query($q4);

// rename new table
$q5 = "RENAME TABLE new_table TO my_table;";
$res5 = $mysqli ->query($q5);

$mysqli ->commit(); // commit changes and re-enable autocommit

$q = "UNLOCK TABLES;";
$res = $mysqli ->query($q); 

为了测试,使用PHPmyadmin,我发布了“SET AUTOCOMMIT = 0; LOCK TABLE my_table WRITE;”查询,然后尝试从my_table删除一些东西,我能够这样做。我想阻止这个。此外,在发出lock语句之后,该过程的其余部分失败,并且没有任何更改。

1 个答案:

答案 0 :(得分:10)

很抱歉答案很长,但这需要分多个部分来解答。

<强> 1。一般情况下锁定InnoDB表{/ 1}}

LOCK TABLES与InnoDB一起使用确实有效,并且可以通过以下两个连接到同一服务器的MySQL CLI实例(由LOCK TABLESmysql-1表示)来演示例。由于对客户的影响,通常应避免在任何生产环境中使用它,但有时它可能是唯一的选择。

创建一个表并用一些数据填充它:

mysql-2

锁定桌子:

mysql-1> create table a (id int not null primary key) engine=innodb;
Query OK, 0 rows affected (0.02 sec)

mysql-1> insert into a (id) values (1), (2), (3);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

尝试从mysql-1> lock tables a write; Query OK, 0 rows affected (0.00 sec) 插入,这将等待锁定:

mysql-2

现在从mysql-2> insert into a (id) values (4); 解锁表格:

mysql-1

最后mysql-1> unlock tables; Query OK, 0 rows affected (0.00 sec) 取消阻止并返回:

mysql-2

<强> 2。使用phpMyAdmin进行测试

使用phpMyAdmin的测试方法无效,因为phpMyAdmin不会在来自其Web界面的查询之间维持与服务器的持久连接。为了使用任何类型的锁定Query OK, 1 row affected (6.30 sec) LOCK TABLES等,您需要在保持锁定时保持连接。

第3。锁定工作期间所需的所有表格

MySQL锁定表的方式,一旦您使用START TRANSACTION显式锁定任何内容,您将无法访问LOCK TABLES期间未明确锁定的任何其他表... { {1}}会话。在上面的示例中,您需要使用:

LOCK

(我假设在子选择中使用的UNLOCK不是拼写错误。)

<强> 4。使用LOCK TABLES my_table WRITE, new_table WRITE, table2 READ;

进行原子表交换

此外,我应该注意,使用table2后跟RENAME TABLE替换现有表将导致表不存在的短暂时间,这可能会使期望它存在的客户端感到困惑。通常做得更好:

DROP TABLE

这将执行两个表的原子交换。