如何在mysql中使用UPDATE原子声明行或资源

Igo*_*any 14 mysql resources transactions atomic

我有一个资源表(比方说汽车),我想原子地声称.然后,我想知道我刚才声称的资源.

如果每个用户有一个资源限制,我可以执行以下操作:

UPDATE cars SET user = 'bob' WHERE user IS NULL LIMIT 1
SELECT * FROM cars WHERE user = 'bob'
Run Code Online (Sandbox Code Playgroud)

这样,我原子地声明了资源,然后我可以看到我刚刚声明了哪一行.

当'bob'可以声称多辆车时,这不起作用.我意识到我可以获得已经由鲍勃声称的汽车列表,声称另一辆汽车,然后SELECT再次看到发生了什么变化,但这感觉很乱.

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

如果做不到这一点,还有其他一些技巧可以原子地声称一行吗?我真的想避免使用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>
Run Code Online (Sandbox Code Playgroud)

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

Sjo*_*erd 11

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

更新查询返回已更改的行数.如果它没有更新任何,你知道该车已被其他人声称.

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


Mah*_*dsi 7

我不确定为什么这些答案中有这么多错误信息,但答案很简单(即使是高级 SQL 主题)。这就是“锁定”在几乎所有 RDBMS 中的作用。确切的语法取决于供应商和版本,以及一些提供的语法,它试图隐藏来自用户的锁定(通常在选择和更新时在同一查询中)。

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

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

之后,当与数据库的同一连接对UPDATE ...先前锁定的同一行发出 an时,该锁将被释放,其他人可以再次访问该行。

因此,假设您有一个作业队列,其中包含一个字段“STATUS”,用于设置每个作业的进度状态。0 代表排队,1 代表进行中,2 代表完成,3 代表失败,等等。

通过发出以下命令,每个运行者都可以原子地获得要运行的作业(这样就不会有两个运行者尝试执行相同的工作):

SELECT ID, * FROM JOBS WHERE STATUS = 0 LIMIT 1 FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)

然后

UPDATE JOBS SET STATUS = 1 WHERE JOBS.ID = X;
Run Code Online (Sandbox Code Playgroud)

然后它可以运行作业,并在完成后更新数据库:

UPDATE JOBS SET STATUS = [2|3] WHERE JOBS.ID = X;
Run Code Online (Sandbox Code Playgroud)

重要的

从 MySQL 文档:

当事务提交或回滚时,所有​​由 LOCK IN SHARE MODE 和 FOR UPDATE 查询设置的锁都会被释放。

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