【问题标题】:How to handle maximum participants count safely?如何安全地处理最大参与者数?
【发布时间】:2018-05-08 03:25:43
【问题描述】:

我们有:

  • 一张桌子Room
  • 一张桌子Person
  • 中间表PersonRoom

Room 中,我的最大值Person,对应于可以进入房间的最大人数。 像这样:

Room
  id
  maximum_persons
Person
  id
  name
PersonRoom
  id
  room_id
  person_id

每当一个“my_person_id”的人想进入一个“my_room_id”的房间时,我应该这样做:

INSERT INTO PersonRoom (room_id, person_id)
                VALUES (my_room_id, my_person_id)

但是如果房间已满,我应该不能插入一个人,即我应该做类似的事情(我知道这是无效的 SQL):

@TOTAL = SELECT COUNT(*) FROM PersonRoom
         WHERE room_id = my_room_id;
IF @TOTAL < (SELECT maximum_persons) FROM Room WHERE id = my_room_id:
    INSERT INTO PersonRoom (room_id, person_id)
                    VALUES (my_room_id, my_person_id)

我想在数据库端处理这个

  • 是否有针对 SQLite 的解决方案?如果没有,是否有针对 PostgreSQL 的解决方案
  • SQL 是什么?
  • 如果我在一次交易中这样做,如果有很多人尝试同时注册同一个房间,是否会 100% 安全?

【问题讨论】:

    标签: sql postgresql sqlite


    【解决方案1】:

    我只能为 PostgreSQL 说话。

    有两种解决方案:

    1. 在一个事务中运行SELECTINSERT 并在执行SELECT 之前锁定表personroom

      你需要ACCESS EXCLUSIVE锁定模式,这意味着所有这样的操作都是序列化的。这不利于并发,但简单且安全。

    2. 在一个SERIALIZABLE 事务中运行SELECTINSERT

      这将允许并发修改,但仍会阻止在序列化执行顺序中不可能发生的任何事情。如果发生冲突,其中一个事务将收到序列化错误,并且必须重试该事务。

    【讨论】:

    • 您认为我可以使用docs.djangoproject.com/en/dev/ref/models/querysets/… 来应用您的#2 解决方案吗?
    • 不,这无济于事。这与我的第一个解决方案类似,但所采用的锁不足以阻止读者。
    • 我检查了您的答案是否有效,因为它是灵感的来源,并且我已经基于此制作了我的代码。我在这里问了一个问题:stackoverflow.com/questions/47484638/… 想知道我的代码是否安全,但没有答案......
    【解决方案2】:

    在 SQLite 中,您可以使用 trigger 来防止这种情况发生:

    CREATE TRIGGER max_persons_check
    BEFORE INSERT ON PersonRoom
    WHEN (SELECT COUNT(*) FROM PersonRoom WHERE room_id = NEW.room_id)
      >= (SELECT maximum_persons FROM Room WHERE id = NEW.room_id)
    BEGIN
      SELECT RAISE(FAIL, "too many persons in this room");
    END;
    

    或者只是进行查询以在您的代码中检查这一点。 (在 SQLite 中,所有事务都是可序列化的。)

    【讨论】:

    • 非常感谢您的回答,您认为有可能使其与 bot SQLite 和 PostgreSQL 兼容吗?
    • 否; SQLite 和 PostgreSQL 的触发器不兼容。
    • 所以我想我必须自己进行交易并使用@Laurenz Albe (docs.djangoproject.com/en/dev/topics/db/transactions) 的#2 解决方案
    • ... 或使用docs.djangoproject.com/en/dev/ref/models/querysets/… 对tabke 进行专有访问
    • 除非读者在 SQLite 中阻止其他读者,否则该解决方案不可能是正确的。否则两个并发事务可以同时运行触发器,找到满足条件后继续修改表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-23
    • 2018-04-28
    • 1970-01-01
    • 2017-02-18
    相关资源
    最近更新 更多