Ben*_*rer 9 postgresql database-design concurrency
考虑一个座位预订数据库。有 n 个座位的列表,每个座位都有一个属性is_booked。0 表示不是,1 表示是。任何更高的数字都会出现超额预订。
在不允许超额预订的情况下进行多次交易(每个交易将同时预订一组 y 个座位)的策略是什么?
我会简单地选择所有未预订的座位,选择其中 y 个随机选择的一组,全部预订,然后检查该预订是否正确(也就是 is_booked 的数量不超过一个,这将表示预订了座位的另一笔交易和提交),然后提交。否则中止并重试。
这是在 Postgres 中的 Read Committed 隔离级别上运行的。
因为你没有告诉我们你需要什么,所以我会猜测一切,我们会使其适度复杂以简化一些可能的问题。
关于 MVCC 的第一件事是,在高并发系统中,您希望避免表锁定。作为一般规则,如果不为事务锁定表,就无法判断不存在的内容。这给您留下了一个选择:不要依赖INSERT.
我在这里几乎没有留下一个真正的预订应用程序的练习。我们不处理,
这里的关键是在事务开始之前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并级联释放座位。
| 归档时间: |
|
| 查看次数: |
1567 次 |
| 最近记录: |