在搶演唱會門票時,常遇到選完座位要結帳出現該座位已被訂走的訊息。
以後端的角度來說,如何確保座位只會被一位使用者買走而不 block 住整個系統,是基本的要求。
這篇文章探討如何解決重複賣票問題。
假設我們有一張表記錄演唱會門票售出情形
1 | CREATE TABLE tickets ( |
訂購門票的 SQL 用一般的 transaction 寫法:
1 | BEGIN |
這樣的寫法有什麼問題呢? 假如同時有另一位使用者在 Alex 的 transaction 還沒結束時也訂了 id = 1 的票,則這張票會被他買走。
為了避免此情況,可以加上 FOR UPDATE
先查詢:
1 | BEGIN |
FOR UPDATE
會加上 row level lock,避免其它 transaction 更動記錄。
PostgreSQL
如果 database server 是 PostgreSQL,以下的 transaction 也能防止 double booking
1 | BEGIN |
假如有另一位使用者同時執行 transaction,在第一個 transaction commit 後,第二個 transaction UPDATE 不會 overwrite 結果。
PG 在每筆 record 後面會加上 lock 的資訊並放在 heap,當第二個 transaction 在 heap 找到 id = 1 的 record 就能得到目前 lock 的狀態。
在第一個 transaction commit 後,PG 會更新 heap 資訊,讓其它 transaction 在 update 時得到新的狀態。
用這種 implicitly 寫法最好加個註解說明。不同的 RDBMS 對於 isolation level 和 lock 有自己的管理方式,工程師需要了解細節才能設計出穩定的服務。