我有一个名为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相同的行。因此,我将结束对同一行的两次更新。
此查询是否可能出现这种情况?
3条答案
按热度按时间nuypyhwy1#
有了mysql,就有了一个简单的、几乎难以置信的解决方案:
此语法将受更新过程影响的行数限制为1,使用mysql对更新语法的特殊扩展达到了您的目的。
当然,作为一个官方支持的扩展,这是一个原子操作并且是完全线程安全的。
尽管您的代码和问题都没有建议您捕获 * 哪个 * id是更新的,但您可以通过用户定义的变量来捕获它:
从select返回的值将是更新的id,或者如果没有行被更新则为null。
vxqlmq5t2#
仅供参考,所有你可以在任何RDBMS中运行的SQL命令都是线程安全的,只要它是我们所讨论的服务器,并且每个线程都使用自己的连接。事实上,数据库服务器正是为此目的而创建的。
在由两个或多个线程/进程并发访问数据库服务器时可能遇到的唯一问题是,就是死锁。这是一个你可以在程序逻辑中避免的问题。死锁发生在一个连接拥有一个资源的时候(对于数据库的记录或表),并请求另一个,而某个其他连接将以相反的顺序获取相同的两个资源。这两个连接将等待另一个释放它们的资源,理论上这可能会永远延长。MySQL通过授予一个有限时间的锁来解决这个问题。
如果要防止死锁,最简单的解决方案是考虑获取锁的顺序,这样两个连接就不可能彼此面对。
回到你的SQL语句,我的答案和Bohemian的一样,都是完整的。如果你坚持随机的,你也可以:
只是你需要知道
WHERE taken IS NULL
是一件非常糟糕的事情。如果你想搜索一个字段,它应该总是NOT NULL
,特别是在涉及锁的情况下。也许你可以分配0
而不是NULL
,这将大大提高性能!km0tfn4u3#
我建议在“UPDATE”之前执行以下查询
当您在同一连接中多次运行“UPDATE / SELECT”查询时,请确保定义变量“@id”值并将其重置为特定值以管理用例。
如果不这样做,那么如果UPDATE没有更新任何内容,而之前的UPDATE确实有效,那么您可能会得到一个错误的值。