久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

使用數據庫怎么實現一個訂座功能

159次閱讀
沒有評論

共計 3948 個字符,預計需要花費 10 分鐘才能閱讀完成。

自動寫代碼機器人,免費開通

這篇文章給大家介紹使用數據庫怎么實現一個訂座功能,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

第一部分:SKIP LOCKED/NOWAIT 訂座功能實現

訂座在現實生活中是一種很常見的場景,比較常見的有火車票席位選擇,電影院席位選擇等等。那么如何實現訂座功能呢?應用程序可能有很多種不同的實現方式,當然,肯定離不開數據庫。這里將介紹一種純數據庫的實現方式。

設想我們有一張座位表如下:

CREATE TABLE seats (
 seat_no INT PRIMARY KEY,
 booked ENUM(YES ,  NO) DEFAULT  NO ) ENGINE=InnoDB;

表中有 100 個席位,從 0 到 99。例如我們要預定席位 2,3,我們可以先開啟事務,鎖定席位:

START TRANSACTION;SELECT * FROM seats WHERE seat_no IN (2,3) AND booked =  NO  FOR UPDATE;

SELECT… FOR UPDATE 語句返回結果有如下三種情況:

1. 返回成功,并且結果集包含 2 和 3,那么說明鎖定成功。我們可以之行下一步操作,等待支付完成,并更新席位狀態并提交事務,訂座完成。UPDATE seats SET booked = YES WHERE seat_no IN (2,3) COMMIT;

2. 返回成功,但結果集為空,或者只包含 2 或者 3,那么說明鎖定失敗。

3. 很長時間不返回直到返回超時。比如席位 2 或者 3 已經被另一事務鎖定,并且在等待支付完成或者發生其他情況,導致該事務一直未提交 (commit) 或者回滾(rollback)。返回超時默認需要等待 50 秒,我們可以通過修改 innodb_lock_wait_timeout 參數來配置合理的等待時間。超時之后返回的錯誤如下:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

情況 3 對用戶來說,意味著卡死,完全不能接受。為什么會發生等待?在 InnoDB 的鎖系統 (lock system) 中,席位 2 如果被一個事務上了 X(寫鎖)鎖或者 IX 鎖 (意向更新鎖),那么下一個事務要對席位 2 上 X 鎖或者 IX 鎖的事務,就要等待。這是由事務本身的特性(ACID) 決定的。

那么是否有一種方法避免等待以及后續可能發生的超時呢?MySQL 8.0 提供的新功能 SKIP LOCKED/NOWAIT 就可以。SKIP LOCKED 的意思是跳過那些已經被其他事務鎖定了的席位。使用如下 SKIP LOCKED 語句進行席位鎖定,那么返回的結果集可能為空,2 或 3,2 和 3。當結果集不為空時,返回的席位即被鎖定成功。

SELECT * FROM seats WHERE seat_no IN (2,3) AND booked =  NO FOR UPDATE SKIP LOCKED;

NOWAIT 的意思是如果碰到被其他事務鎖定的席位,不等待并直接返回錯誤。使用如下 NOWAIT 語句進行席位鎖定,那么返回結果集 2 和 3,要么返回錯誤。

SELECT * FROM seats WHERE seat_no IN (2,3) AND booked =  NO FOR UPDATE NOWAIT;

如果返回錯誤,如下:

ERROR 3572 (HY000): Do not wait for lock.

如果成功鎖定兩個席位,通過如下語句查詢鎖系統的狀態:

SELECT thread_id, object_name, lock_type, lock_mode, lock_data, lock_status FROM performance_schema.data_locks;+-----------+-------------+-----------+-----------+-----------+-------------+| thread_id | object_name | lock_type | lock_mode | lock_data | lock_status |
+-----------+-------------+-----------+-----------+-----------+-------------+| 43 | seats | TABLE | IX | NULL | GRANTED |
| 43 | seats | RECORD | X | 2 | WAITING |
| 42 | seats | TABLE | IX | NULL | GRANTED |
| 42 | seats | RECORD | X | 2 | GRANTED |
| 42 | seats | RECORD | X | 3 | GRANTED |
+-----------+-------------+-----------+-----------+-----------+-------------+

SKIP LOCKED 還可以很方便的用來進行隨機分配席位。例如我們只需要鎖定兩個空的席位就可以通過如下語句實現。

SELECT * FROM seats WHERE booked =  NO  LIMIT 2 FOR UPDATE SKIP LOCKED;

SKIP LOCKED/NOWAIT 功能只針對行鎖(record lock),不包括表鎖(table lock),元數據鎖(metadata lock/MDL)。因此,帶有 SKIP LOCKED/NOWAIT 的查詢語句依然可能會因為表鎖或元數據庫鎖而阻塞。元數據鎖是 MySQL Server 層用來保護數據庫對象的并發訪問的一致性而創建的,數據庫對象不僅包括表,同時包括庫,函數,存儲過程,觸發器,事件等等。表和行鎖是 InnoDB 存儲引擎內部為了保證事務的一致性而創建的不同粒度的鎖。

另外,SKIP LOCKED/NOWAIT 還可以配合 FOR SHARE 使用,并且可以與單表綁定。例如:

SELECT seat_noFROM seats JOIN seat_rows USING ( row_no )WHERE seat_no IN (2,3) AND seat_rows.row_no IN (12)AND booked =  NO FOR UPDATE OF seats SKIP LOCKEDFOR SHARE OF seat_rows NOWAIT;

第二部分:SKIP LOCKED/NOWAIT 在 InnoDB 中的代碼實現

在 InnoDB 中,實現 SKIP LOCKED/NOWAIT 具體實現如下:

1. 增加新的查詢模式 enum select_mode {SELECT_ORDINARY = 0, /* default behaviour / SELECT_SKIP_LOCKED, / skip the row if row is locked / SELECT_NO_WAIT / return immediately if row is locked */};

2. 在查詢開始前,設置查詢模式 ha_innobase::store_lock(): /* Set select mode for SKIP LOCKED / NO_WAIT */ switch (lock_type) {case TL_READ_SHARED_SKIP_LOCKED: case TL_WRITE_SKIP_LOCKED: m_prebuilt- select_mode = SELECT_SKIP_LOCKED; break; case TL_READ_SHARED_NO_WAIT: case TL_WRITE_NO_WAIT: m_prebuilt- select_mode = SELECT_NO_WAIT; break; default: m_prebuilt- select_mode = SELECT_ORDINARY; break;}

3. 上鎖函數中,如果記錄已被鎖定,針對對不同查詢模式進行相應處理:lock_rec_lock_slow(): if (wait_for != NULL) {switch (sel_mode) {case SELECT_SKIP_LOCKED: err = DB_SKIP_LOCKED; break; case SELECT_NO_WAIT: err = DB_LOCK_NOWAIT; break;

4. 查詢中對上鎖結果進行處理:row_search_mvcc(): case DB_SKIP_LOCKED: goto next_rec; 對 DB_LOCK_NOWAIT 的處理則是回滾當前語句(statement),見函數 row_mysql_handle_errors()。

5. 二級索引 (secondary index) 的處理 在 InnoDB 中,對表中記錄的鎖定分兩種情況。第一種是查詢使用是聚集索引(cluster index),那么直接對聚集索引的記錄上鎖;第二中是查詢使用的是二級索引,那么首先對二級索引的記錄上鎖,然后根據二級索引的記錄,找到對應的聚集索引記錄進行上鎖。所以,對于第一部分訂座的席位表中,如果存在二級索引,對于鎖定表中一條記錄而言,最終鎖定成功與否,還是以鎖定聚集索引記錄為準。

SKIP LOCKED/NOWAIT 可以非常高效地實現訂座這個場景,作為 InnoDB 部分(WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED)的原作者,我也期待著大家來分享該功能更多的使用場景。

關于使用數據庫怎么實現一個訂座功能就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向 AI 問一下細節

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-12-04發表,共計3948字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 平泉县| 庄浪县| 任丘市| 新平| 高碑店市| 措美县| 钟祥市| 淮北市| 平安县| 美姑县| 长春市| 阆中市| 常州市| 桂林市| 松原市| 揭东县| 台东县| 大田县| 岐山县| 苗栗县| 辛集市| 抚松县| 亳州市| 胶南市| 罗山县| 自治县| 汽车| 饶阳县| 张家川| 哈巴河县| 定西市| 临武县| 崇明县| 乌拉特后旗| 韩城市| 木兰县| 西畴县| 濉溪县| 乐业县| 库伦旗| 醴陵市|