mysql 此更新、选择组合查询线程安全吗?

pqwbnv8z  于 2023-03-17  发布在  Mysql
关注(0)|答案(3)|浏览(168)

我有一个名为seats的表,它的模式如下所示
ID,已拍摄
对于每个用户,我随机取一个untaked id并分配给该用户,这里为了简单起见,我设taked =1。

update seats u inner join  (
        SELECT id from seats  
        where taken is null limit 1) s 
   on s.id = u.id set taken = 1;

这个查询获取一个随机的位置,其taked标志为空,并将该位置的标志设为1。虽然这个查询运行良好,但这个线程安全吗?
考虑以下场景,我有两个并行用户。对于user1,我选择了行X,就在运行更新查询之前,user2签入,对于该用户,选择查询返回与user1相同的行。因此,我将结束对同一行的两次更新。
此查询是否可能出现这种情况?

nuypyhwy

nuypyhwy1#

有了mysql,就有了一个简单的、几乎难以置信的解决方案:

update seats set taken = 1
where taken is null
limit 1

此语法将受更新过程影响的行数限制为1,使用mysql对更新语法的特殊扩展达到了您的目的。
当然,作为一个官方支持的扩展,这是一个原子操作并且是完全线程安全的。
尽管您的代码和问题都没有建议您捕获 * 哪个 * id是更新的,但您可以通过用户定义的变量来捕获它:

update seats set
taken = 1,
id = (@id := id)
where taken is null
limit 1;

select @id id;

从select返回的值将是更新的id,或者如果没有行被更新则为null。

vxqlmq5t

vxqlmq5t2#

仅供参考,所有你可以在任何RDBMS中运行的SQL命令都是线程安全的,只要它是我们所讨论的服务器,并且每个线程都使用自己的连接。事实上,数据库服务器正是为此目的而创建的。
在由两个或多个线程/进程并发访问数据库服务器时可能遇到的唯一问题是,就是死锁。这是一个你可以在程序逻辑中避免的问题。死锁发生在一个连接拥有一个资源的时候(对于数据库的记录或表),并请求另一个,而某个其他连接将以相反的顺序获取相同的两个资源。这两个连接将等待另一个释放它们的资源,理论上这可能会永远延长。MySQL通过授予一个有限时间的锁来解决这个问题。
如果要防止死锁,最简单的解决方案是考虑获取锁的顺序,这样两个连接就不可能彼此面对。
回到你的SQL语句,我的答案和Bohemian的一样,都是完整的。如果你坚持随机的,你也可以:

UPDATE seats
SET
    taken = 1
WHERE
    taken IS NULL
ORDER BY RAND()
LIMIT 1

只是你需要知道WHERE taken IS NULL是一件非常糟糕的事情。如果你想搜索一个字段,它应该总是NOT NULL,特别是在涉及锁的情况下。也许你可以分配0而不是NULL,这将大大提高性能!

km0tfn4u

km0tfn4u3#

我建议在“UPDATE”之前执行以下查询

SET @id = NULL

当您在同一连接中多次运行“UPDATE / SELECT”查询时,请确保定义变量“@id”值并将其重置为特定值以管理用例。
如果不这样做,那么如果UPDATE没有更新任何内容,而之前的UPDATE确实有效,那么您可能会得到一个错误的值。

相关问题