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.
我不确定为什么这些答案中有这么多错误信息,但答案很简单(即使是高级 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;