sql - Postgres 对房间内用户限制的设计解决方案(竞争条件)
问题描述
我有一个聊天应用程序,其中用户可以随时自由加入/离开。房间一次限制为 8 位用户。
为简化起见,我现在有这种关系:
用户 ->(多对一)-> 房间
我正在通过查询来检查房间是否已满
SELECT COUNT(*) FROM users WHERE room_id = x
在插入用户之前,这在正常情况下有效。
但是,如果每个人都同时加入,那么它就会进入竞争状态,并且会绕过限制。我应该如何解决这个问题?Postgres 适合这种操作吗?
解决方案
虽然不想不友善,但之前的答案远非最佳。
虽然你可以做到这一点...
LOCK TABLE users IN ACCESS EXCLUSIVE MODE;
这显然是相当沉重的,并且会阻止对用户表的所有更新,无论是否与房间更改有关。
一种更轻松的方法是只锁定您关心的数据。
-- Move user 456 from room 122 to room 123
BEGIN;
SELECT true FROM rooms WHERE id = 123 FOR UPDATE;
SELECT true FROM users WHERE id = 456 AND room_id = 122 FOR UPDATE;
-- If either of the above failed to return a row, the starting condition of your database is not what you thought. Rollback
SELECT count(*) FROM users WHERE room_id = 123;
-- check count looks ok
UPDATE users SET room_id = 123 WHERE id = 456;
COMMIT;
这将锁定两个关键项目:
- 搬去的新房间。
- 用户在房间之间移动。
既然你不关心人们被搬出房间,这应该足够了。
如果两个独立的连接试图同时将不同的人移动到同一个房间,一个将被迫等待,直到另一个事务提交或回滚并释放它的锁。如果两个独立的连接尝试更新同一个人,同样的情况也会发生。
至于其他答案
当然,在表被锁定期间,如果你的应用程序中的某个线程试图写用户,它会返回一个错误。
不,不会的。除非您持有锁足够长的时间以超时,或者尝试使用自己的锁并告诉它不要等待。
我认为您可能应该同步调用应用程序的访问和写入
关系数据库已被明确设计为处理来自多个客户端的并发访问。这是他们无可争辩的擅长的事情之一。如果您已经在使用 RDBMS 并实现自己的并发控制,那么您要么处于特殊情况,要么没有正确使用您的 RDBMS。
推荐阅读
- python - 如何从我需要的文本中跳过或截断字符或符号。用美丽的汤刮网
- mongodb - MongoDB Atlas - 地理位置
- c# - NotifyCollectionChangedAction.Reset 不起作用
- android - 重定向热点网络中的流量
- reactjs - React-Native 上下文尚未填充
- bash - 如何使用 sed 或可能的 grep 替换整个字符串
- mysql - 为 sql 中的每个“IN”语句获取 1 个结果
- javascript - 异步等待未按预期工作:ReactJS
- sql - 如何过滤以获取列的值正是排序后的某个数组,Bigquery
- c# - 如何在 Web API 中对冲突响应的错误消息进行单元测试