2010-03-17 29 views
13

我有一个资源表(可以说汽车),我想自动声明。然后我需要关于我刚才声称的资源的信息。如何使用mysql中的UPDATE自动声明一行或资源

如果有每一个用户资源的极限,我可以做下面的技巧:

UPDATE cars SET user = 'bob' WHERE user IS NULL LIMIT 1 
SELECT * FROM cars WHERE user = 'bob' 

这样,我要求原子资源,然后我可以看到哪一行我只是声称。

当'bob'可以声明多辆车时,这不起作用。我意识到我可以得到一份已经被bob声称的汽车列表,声称另一个,然后SELECT再次看看发生了什么变化,但这感觉hackish。

我想知道的是,有没有什么方法可以查看哪些行我刚更新了我的上一次更新?

如果失败了,是否还有其他一些技巧来自动声明一行?我真的想避免使用SERIALIZABLE隔离级别。如果我做这样的事情:

1 SELECT id FROM cars WHERE user IS NULL 
2 <here, my PHP or whatever picks a car id> 
3 UPDATE cars SET user = 'bob' WHERE id = <the one i picked> 

REPEATABLE READ这里就够了吗?换句话说,我能保证一些其他交易不会声称我的软件在第2步中选择了哪一行吗?

+0

我发现这个:http://stackoverflow.com/questions/1821142/atomically-mark-and-return-a-group-of-rows-in-database,但它是询问有关SQL服务器 - 我无法找到一个mySQL等效 – 2010-03-17 08:26:21

回答

8

UPDATE cars SET user ='bob'WHERE id = 123 AND user IS NULL;

更新查询返回更改的行数。如果它没有更新,你就知道这辆车已经被别人抢走了。

或者,您可以使用SELECT ... FOR UPDATE。

+1

感谢您让我知道SELECT ... FOR UPDATE - 这正是我所期待的。 – 2010-03-17 13:24:54

1

您可以使用的一件事是SELECT FOR UPDATE。这将让你做你的选择,知道你选择了什么,然后更新这些值。交易完成时锁定被释放。

<?php 
$link = mysqli_connect("localhost", "my_user", "my_password", "test"); 

mysqli_autocommit($link, FALSE); 

$result = mysqli_query($link, "SELECT id FROM cars WHERE user IS NULL"); 

// do something with the results 

mysqli_query($link, "UPDATE cars SET user = 'bob' WHERE id = <the one that was picked>"); 

mysqli_commit($link); 

mysqli_close($link); 
?> 
+2

你的例子是错误的,你需要在UPDATE查询上追加“FOR UPDATE”。也很重要:这只适用于InnoDB作为存储引擎,而不适用于MyISAM。存储类型可以用“显示表​​状态”显示。您可以使用“ALTER TABLE tablename ENGINE = INNODB;”更改存储引擎 – seb 2013-06-17 11:48:17

1
update cars 
set @id = id, user= 'bob' 
where user is null 

被保证是原子和@id会告诉你什么是你更新的最后一排。

+0

这真的很聪明。我肯定会在某些时候使用这个,虽然在这里我想我更喜欢select ... for update – 2010-03-17 13:23:39

+0

由于某种原因,这给了我错误1064(参见http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-更新和-GET-从-A-mysql的数据库)。是否允许在'UPDATE'的'SET'部分设置用户定义的变量? – 2015-09-16 18:35:10

+0

看来你需要使用黑客来使你的建议工作,因为以这种方式设置用户变量是不允许的。详情在这里:http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-update-and-get-from-a-mysql-database/32617172#32617172 – 2015-09-16 19:38:50

0

我不确定为什么这些答案中有太多的错误信息,但答案很简单(即使是高级SQL主题)。这就是几乎所有RDBMS中的“锁定”。确切的语法取决于供应商和版本,并且一些提供尝试从用户隐藏锁的语法(通常当select和update都在同一个查询中时)。

对于MySQL,首先使用SELECT ... FROM ... FOR UPDATE;,它告诉数据库在它返回的每条记录上设置排它锁。

重要不锁定更多的行比你绝对需要!尽可能细化“SELECT FOR UPDATE”查询,并自由使用“WHERE”和“LIMIT”子句。

之后,当与数据库相同的连接在先前锁定的相同行上发出UPDATE ...时,该锁被释放,其他人可能再次访问该行。

假设您有一个工作队列,并带有一个字段“STATUS”,用于设置每个工作的进度状态。 0表示排队,1表示进行中,2表示完成,3表示失败等。

每个跑步者都可以自动获得一份工作来跑步(这样就不会有两名跑步者尝试工作同一份工作)通过发出如下:

SELECT ID, * FROM JOBS WHERE STATUS = 0 LIMIT 1 FOR UPDATE;

然后

UPDATE JOBS SET STATUS = 1 WHERE JOBS.ID = X;

那么做时,它可以运行作业,并更新数据库:

UPDATE JOBS SET STATUS = [2|3] WHERE JOBS.ID = X;

重要

从MySQL文档:

由SHARE模式锁定和FOR UPDATE查询设置的所有锁在事务提交或回滚被释放。

SELECT FOR UPDATE如果启用自动提交,则不锁定记录。要么禁用自动提交或(最好是)使用START TRANSACTION; SELECT ... FROM ... FOR UPDATE; UPDATE ...; END TRANSACTION;