MySQL表锁定:持有者读写,其他会话只能读取?

时间:2011-10-24 21:39:08

标签: mysql sql transactions table-locking

是否可以锁定表格以使持有者可以读写,而其他会话只能读取?

documentation似乎暗示读锁允许每个人只读,而写锁只允许持有者读写,而其他会话无法访问。似乎让持有者能够读写,而其他只能读取的会话将是一种非常常见的行为 - 也许是最常见的行为。

实施这种情况的性能可能太高了吗?

4 个答案:

答案 0 :(得分:3)

看看LOCK IN SHARE MODE

这将允许您设置非阻塞读锁。

但请记住,这可能导致死锁!确保您对过时信息的流程没问题。

答案 1 :(得分:3)

现有答案中有许多正确的单词,但似乎没有人给出明确的答案。我会试试。

正如您在LOCK TABLES的文档中已经看到的那样,它不能用于此目的,因为对于READ锁定:

  

持有锁的会话可以读取表(但不能写入)。

WRITE锁定:

  

只有持有锁的会话才能访问该表。在解除锁定之前,没有其他会话可以访问它。

这就是使用任意引擎表几乎无法实现的效果,但它可以通过事务引擎实现,即InnoDB。

让我们考虑一下,单个会话在表上保持一个持久的写锁定意味着什么意味着其他表可以根据事务从表中读取数据。这意味着我们有一个开放的长生存事务(让它成为W事务),它锁定一个表进行修改,而其他事务(在其他会话中)可以读取已修改但尚未提交的数据。就隔离级别而言,这意味着我们应该将默认隔离级别设置为READ-UNCOMMITTED,这样我们就不必更改每个新会话的隔离级别:

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

但是我们的事务W应该使用更强的隔离级别,否则我们不能对我们的表应用任何锁定。 READ-COMMITTED不够强大,但REPEATABLE-READ正是我们想要的。这是为了启动W事务,我们应该为当前会话设置事务级别:

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

现在,如何锁定整个表。让我们创建一个表:

CREATE TABLE t (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  val VARCHAR(45) NOT NULL,
  PRIMARY KEY (id)
) ENGINE = InnoDB;

LOCK IN SHARE MODE不是我们想要的:

  

如果[已读取]的任何行被另一个尚未提交的事务更改,则查询将等待该事务结束,然后使用最新值。

LOCK FOR UPDATE似乎做了我们需要的事情:

  

SELECT ... FOR UPDATE锁定行和任何相关的索引条目。

现在我们只需要锁定行。我们最简单的方法是锁定主键。 COUNT(*)对InnoDB进行完整的索引扫描(因为InnoDB不知道确切的行数)。

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT COUNT(*) FROM t FOR UPDATE;
INSERT INTO t VALUES (NULL, '');

现在您可以打开其他会话并尝试从表中读取数据,并尝试添加或修改这些会话中的现有数据。

问题是,您应该在W中提交修改,并且一旦提交事务,锁就会被释放,所有等待的插入或更新也会被应​​用,即使您提交了它也是如此用:

 COMMIT AND CHAIN; SELECT COUNT(*) FROM ti FOR UPDATE;

故事的寓意是拥有两个MySQL帐户要容易得多:a)编写具有INSERT,UPDATE和DELETE GRANT permissions的帐户,以及b)读取没有的帐户。

答案 2 :(得分:0)

SELECT ... FOR UPDATE,它会锁定执行SELECT ... FOR UPDATE的其他来电者的行,但不会为只做SELECT的任何人锁定。 UPDATE也将等待锁定。

当您想要获取值然后推送更新而没有任何人更改值而您没有注意到时,这非常有用。小心,添加太多的会让你陷入僵局。

答案 3 :(得分:-1)

您可能会发现InnoDB引擎默认执行您所需的操作:写入不会阻止读取。您需要小心事务隔离级别,以便在需要时可以使用写入。