Karena Anda tidak memberi tahu kami apa yang Anda butuhkan, saya akan menebak semuanya, dan kami akan membuatnya cukup rumit untuk menyederhanakan beberapa pertanyaan yang mungkin.
Hal pertama tentang MVCC adalah bahwa dalam sistem yang sangat bersamaan Anda ingin menghindari penguncian tabel. Sebagai aturan umum, Anda tidak bisa mengatakan apa yang tidak ada tanpa mengunci tabel untuk transaksi. Itu membuat Anda satu pilihan: jangan mengandalkan INSERT
.
Saya meninggalkan sedikit sebagai latihan untuk aplikasi pemesanan nyata di sini. Kami tidak menangani,
- Overbooking (sebagai fitur)
- Atau apa yang harus dilakukan jika tidak ada kursi x-tersisa.
- Membangun ke pelanggan dan transaksi.
Kuncinya di sini adalah di UPDATE.
Kami mengunci hanya baris untuk UPDATE
sebelum transaksi dimulai. Kita dapat melakukan ini karena kita telah memasukkan semua tiket kursi untuk dijual di meja event_venue_seats
,.
Buat skema dasar
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)
);
Data Uji
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);
Dan sekarang untuk Transaksi Pemesanan
Sekarang kami memiliki hard disk eventid ke salah satunya, Anda harus mengatur ini untuk acara apa pun yang Anda inginkan, customerid
dan txnid
pada dasarnya membuat kursi disediakan dan memberi tahu Anda siapa yang melakukannya. The FOR UPDATE
adalah kunci. Baris-baris itu dikunci selama pembaruan.
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;
Pembaruan
Untuk pemesanan waktunya
Anda akan menggunakan reservasi berjangka waktu. Seperti ketika Anda membeli tiket konser, Anda memiliki menit M untuk mengkonfirmasi pemesanan, atau orang lain mendapatkan kesempatan - Neil McGuigan 19 menit yang lalu
Apa yang akan Anda lakukan di sini adalah mengatur booking.event_venue_seats.txnid
sebagai
txnid int REFERENCES transactions ON DELETE SET NULL
Kedua, pengguna menyimpan seet, UPDATE
menempatkannya di txnid. Tabel transaksi Anda terlihat seperti ini.
CREATE TABLE transactions (
txnid serial PRIMARY KEY,
txn_start timestamp DEFAULT now(),
txn_expire timestamp DEFAULT now() + '5 minutes'
);
Lalu dalam setiap menit Anda berlari
DELETE FROM transactions
WHERE txn_expire < now()
Anda dapat meminta pengguna untuk memperpanjang timer saat mendekati kedaluwarsa. Atau, biarkan saja menghapus txnid
dan turun membebaskan kursi.
Saya pikir ini dapat dicapai dengan menggunakan meja ganda mewah dan beberapa kendala.
Mari kita mulai dengan beberapa struktur (tidak sepenuhnya dinormalisasi):
Pemesanan meja, alih-alih memiliki
is_booked
kolom, sudah mendapatbooker
kolom. Jika nol, kursi tidak dipesan, jika tidak ini adalah nama (id) dari pemesan.Kami menambahkan beberapa contoh data ...
Kami membuat tabel kedua untuk pemesanan, dengan satu batasan:
Tabel kedua ini akan berisi COPY dari tuple (session_id, seat_number, booker), dengan satu
FOREIGN KEY
kendala; yang tidak akan memungkinkan pemesanan asli DIPERBARUI oleh tugas lain. [Dengan asumsi bahwa tidak pernah ada dua tugas yang berurusan dengan pembukuan yang sama ; jika itu masalahnya,task_id
kolom tertentu harus ditambahkan.]Setiap kali kita perlu melakukan pemesanan, urutan langkah-langkah yang diikuti dalam fungsi berikut menunjukkan caranya:
Untuk benar-benar melakukan pemesanan, program Anda harus mencoba menjalankan sesuatu seperti:
Ini bergantung pada dua fakta 1.
FOREIGN KEY
Kendala tidak akan membiarkan data menjadi rusak . 2. Kami MEMPERBARUI tabel pemesanan, tetapi hanya MASUKKAN (dan tidak pernah MEMPERBARUI ) pada booking_with_bookers satu (tabel kedua).Tidak perlu
SERIALIZABLE
tingkat isolasi, yang akan sangat menyederhanakan logika. Namun dalam praktiknya, deadlock diharapkan, dan program yang berinteraksi dengan database harus dirancang untuk menanganinya.sumber
SERIALIZABLE
karena jika dua book_sessions dieksekusi pada saat yang sama makacount(*)
dari txn kedua dapat membaca tabel sebelum book_session pertama selesai dengan nyaINSERT
. Sebagai aturan umum, tidak aman untuk menguji tidak adanya wo /SERIALIZABLE
.Saya akan menggunakan
CHECK
batasan untuk mencegah overbooking dan menghindari penguncian baris secara eksplisit.Tabel dapat didefinisikan seperti ini:
Pemesanan sejumlah kursi dilakukan oleh satu orang
UPDATE
:Kode Anda harus memiliki logika coba lagi. Biasanya, coba jalankan ini
UPDATE
. Transaksi akan terdiri dari yang iniUPDATE
. Jika tidak ada masalah, Anda dapat yakin bahwa seluruh kartu telah dipesan. Jika Anda mendapatkan pelanggaran PERIKSA kendala, Anda harus mencoba lagi.Jadi, ini adalah pendekatan yang optimis.
UPDATE
, karena kendala (yaitu mesin DB) melakukannya untuk Anda.sumber
Pendekatan 1s - PEMBARUAN Tunggal:
Pendekatan 2 - LOOP (plpgsql):
Pendekatan 3 - Tabel antrian:
Transaksi itu sendiri tidak memperbarui tabel kursi. Mereka semua MASUKKAN permintaan mereka ke dalam tabel antrian.
Sebuah proses yang terpisah mengambil semua permintaan dari tabel antrian dan menangani mereka, dengan mengalokasikan kursi untuk pemohon.
Keuntungan:
- Dengan menggunakan INSERT, penguncian / pertikaian dihilangkan
- Tidak ada pemesanan berlebih dipastikan dengan menggunakan satu proses untuk alokasi kursi
Kekurangan:
- Alokasi kursi tidak langsung
sumber