并发团体预订的策略?

Ben*_*rer 9 postgresql database-design concurrency

考虑一个座位预订数据库。有 n 个座位的列表,每个座位都有一个属性is_booked。0 表示不是,1 表示是。任何更高的数字都会出现超额预订。

在不允许超额预订的情况下进行多次交易(每个交易将同时预订一组 y 个座位)的策略是什么?

我会简单地选择所有未预订的座位,选择其中 y 个随机选择的一组,全部预订,然后检查该预订是否正确(也就是 is_booked 的数量不超过一个,这将表示预订了座位的另一笔交易和提交),然后提交。否则中止并重试。

这是在 Postgres 中的 Read Committed 隔离级别上运行的。

Eva*_*oll 6

因为你没有告诉我们你需要什么,所以我会猜测一切,我们会使其适度复杂以简化一些可能的问题。

关于 MVCC 的第一件事是,在高并发系统中,您希望避免表锁定。作为一般规则,如果不为事务锁定表,就无法判断不存在的内容。这给您留下了一个选择:不要依赖INSERT.

我在这里几乎没有留下一个真正的预订应用程序的练习。我们不处理,

  • 超额预订(作为一项功能)
  • 或者如果没有 x 剩余座位该怎么办。
  • 对客户和交易进行扩建。

这里的关键是在事务开始之前UPDATE.我们只锁定行UPDATE。我们可以这样做,因为我们已经在表中插入了所有待售的座位票event_venue_seats

创建基本模式

CREATE SCHEMA booking;
CREATE TABLE booking.venue (
  venueid    serial PRIMARY KEY,
  venue_name text   NOT NULL
  -- stuff
);
CREATE TABLE booking.seats (
  seatid        serial PRIMARY KEY,
  venueid       int    REFERENCES booking.venue,
  seatnum       int,
  special_notes text,
  UNIQUE (venueid, seatnum)
  --stuff
);
CREATE TABLE booking.event (
  eventid         serial     PRIMARY KEY,
  event_name      text,
  event_timestamp timestamp  NOT NULL
  --stuff
);
CREATE TABLE booking.event_venue_seats (
  eventid    int     REFERENCES booking.event,
  seatid     int     REFERENCES booking.seats,
  txnid      int,
  customerid int,
  PRIMARY KEY (eventid, seatid)
);
Run Code Online (Sandbox Code Playgroud)

测试数据

INSERT INTO booking.venue (venue_name)
VALUES ('Madison Square Garden');

INSERT INTO booking.seats (venueid, seatnum)
SELECT venueid, s
FROM booking.venue
  CROSS JOIN generate_series(1,42) AS s;

INSERT INTO booking.event (event_name, event_timestamp)
VALUES ('Evan Birthday Bash', now());

-- INSERT all the possible seat permutations for the first event
INSERT INTO booking.event_venue_seats (eventid,seatid)
SELECT eventid, seatid
FROM booking.seats
INNER JOIN booking.venue
  USING (venueid)
INNER JOIN booking.event
  ON (eventid = 1);
Run Code Online (Sandbox Code Playgroud)

现在是预订交易

现在我们将 eventid 硬编码为 1,您应该将其设置为您想要的任何事件,customerid并且txnid基本上保留座位并告诉您是谁做的。将FOR UPDATE是关键。这些行在更新期间被锁定。

UPDATE booking.event_venue_seats
SET customerid = 1,
  txnid = 1
FROM (
  SELECT eventid, seatid
  FROM booking.event_venue_seats
  JOIN booking.seats
    USING (seatid)
  INNER JOIN booking.venue
    USING (venueid)
  INNER JOIN booking.event
    USING (eventid)
  WHERE txnid IS NULL
    AND customerid IS NULL
    -- for which event
    AND eventid = 1
  OFFSET 0 ROWS
  -- how many seats do you want? (they're all locked)
  FETCH NEXT 7 ROWS ONLY
  FOR UPDATE
) AS t
WHERE
  event_venue_seats.seatid = t.seatid
  AND event_venue_seats.eventid = t.eventid;
Run Code Online (Sandbox Code Playgroud)

更新

对于定时预订

您将使用定时预订。就像你买音乐会门票时,你有 M 分钟的时间来确认预订,或者其他人有机会 - Neil McGuigan 19 分钟前

你会在这里做的是设置booking.event_venue_seats.txnid

txnid int REFERENCES transactions ON DELETE SET NULL
Run Code Online (Sandbox Code Playgroud)

用户保留UPDATESeet的第二个,则放入 txnid。您的事务表看起来像这样。

CREATE TABLE transactions (
  txnid       serial PRIMARY KEY,
  txn_start   timestamp DEFAULT now(),
  txn_expire  timestamp DEFAULT now() + '5 minutes'
);
Run Code Online (Sandbox Code Playgroud)

然后在你奔跑的每一分钟

DELETE FROM transactions
WHERE txn_expire < now()
Run Code Online (Sandbox Code Playgroud)

您可以在接近到期时提示用户延长计时器。或者,只是让它删除txnid并级联释放座位。