【问题标题】:How can I lock an InnoDB table to prevent updates while that table is being copied?如何在复制该表时锁定 InnoDB 表以防止更新?
【发布时间】:2012-12-24 05:50:03
【问题描述】:

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

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

这是我尝试过的,但它似乎不起作用。

$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 语句后,程序的其余部分将失败并且没有任何改变。

【问题讨论】:

  • “似乎不起作用”您进行了哪些调试?
  • 我编辑了这个问题,让你知道什么是失败的。我只想知道锁定 INNODB 表以防止更新的正确过程。
  • 这样更好。你检查权限了吗?您确定删除已立即应用而不是“排队”吗?您是否调查过关闭自动提交是否会导致问题?我从来没有发现自己必须这样做。
  • 权限没问题。删除自动提交没有区别

标签: mysql locking innodb


【解决方案1】:

对不起,答案很长,但这需要分多个部分来回答。

1.在一般情况下使用LOCK TABLES 锁定 InnoDB 表

在 InnoDB 中使用LOCK TABLES 确实有效,并且可以在下面的示例中通过连接到同一服务器的两个 MySQL CLI 实例(由mysql-1mysql-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 解除阻塞并返回:

Query OK, 1 row affected (6.30 sec)

2。使用 phpMyAdmin 进行测试

您使用 phpMyAdmin 的测试方法无效,因为 phpMyAdmin 在来自其 Web 界面的查询之间不保持与服务器的持久连接。为了使用任何类型的锁定LOCK TABLESSTART TRANSACTION 等,您需要在持有锁定时保持连接。

3.锁定工作期间需要的所有表

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

LOCK TABLES my_table WRITE, new_table WRITE, table2 READ;

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

4.使用RENAME TABLE的原子表交换

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

CREATE TABLE t_new (...);
<Populate t_new using some method>
RENAME TABLE t TO t_old, t_new TO t;
DROP TABLE t_old;

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

【讨论】:

  • 我的 MySQL 抱怨说我在重命名时可能没有锁定表。 “由于您有活动的锁定表或活动事务,因此无法执行给定的命令”知道我做错了什么吗?
  • 您经常会看到针对初学者问题的简单答案,投票次数超过 500 次。然后你会看到一个很好的答案,一个相关的好问题在 4 年内只被投票了 9 次!谢谢,特别是 #4 对我来说是新的,它帮助我解决了潜在的竞争条件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 2016-08-14
  • 1970-01-01
  • 2020-10-16
  • 2015-08-31
相关资源
最近更新 更多