5

我相当确定这有一个简单的解决方案,但到目前为止我还没有找到它。提供了一个隔离级别设置为 SERIALIZABLE 的 InnoDB MySQL 数据库,并给出以下操作:

BEGIN WORK;
SELECT * FROM users WHERE userID=1;
UPDATE users SET credits=100 WHERE userID=1;
COMMIT;

我想确保一旦事务中的 select 发出,对应于 userID=1 的行就被锁定以供读取,直到事务完成。就目前而言,如果事务正在进行,则对该行的 UPDATE 将等待事务完成,但 SELECT 只会读取先前的值。我知道这是在这种情况下的预期行为,但我想知道是否有办法锁定行,这样 SELECT 也会等到事务完成返回值?

我正在寻找的原因是,在某个时候,如果有足够多的并发用户,可能会发生这样的情况:当前一个交易正在进行时,其他人会读取“信用”来计算其他东西。理想情况下,其他人运行的代码应该等待事务完成才能使用新值,否则可能会导致不可逆转的异步问题。

请注意,我不想锁定整个表进行读取,只锁定特定行。

此外,我可以在表中添加一个布尔“锁定”字段并将其设置为 1 每次我开始事务时,但我并不觉得这是这里最优雅的解决方案,除非绝对没有其他方法可以直接通过mysql处理。

4

2 回答 2

3

我找到了一种解决方法,特别是:

SELECT ... LOCK IN SHARE MODE 在读取的行上设置共享模式锁。共享模式锁使其他会话能够读取行但不能修改它们。读取的行是最新可用的,因此如果它们属于尚未提交的另一个事务,则读取会阻塞,直到该事务结束。

来源

似乎可以在依赖事务数据的关键 SELECT 语句中包含 LOCK IN SHARE MODE,并且它们确实会在检索行之前等待当前事务完成。为此,事务必须明确使用 FOR UPDATE(与我给出的原始示例相反)。例如,给定以下内容:

BEGIN WORK;
SELECT * FROM users WHERE userID=1 FOR UPDATE;
UPDATE users SET credits=100 WHERE userID=1;
COMMIT;

我可以在代码中的其他任何地方使用:

SELECT * FROM users WHERE userID=1 LOCK IN SHARE MODE;

由于该语句未包装在事务中,因此锁会立即释放,因此对后续查询没有影响,但是如果在事务中选择了涉及 userID=1 的行进行更新,则该语句将等待事务完成,这正是我想要的。

于 2012-08-31T03:35:45.473 回答
2

您可以尝试SELECT ... FOR UPDATE锁定读取。

SELECT ... FOR UPDATE 读取最新的可用数据,在它读取的每一行上设置排他锁。因此,它设置了与搜索的 SQL UPDATE 将在行上设置的锁相同的锁。

请浏览以下网站:http ://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

于 2012-04-27T17:14:28.883 回答