標籤: Repeatable Read

淺談MySQL隔離層級為RR(可重復讀)時不能避免Phantom Read(幻讀)

SQL資料庫

情境模擬

我們先來破冰一下,幫大家再回憶一次什麼是Phantom Read(幻讀)。假設今天水果行採用了一個庫存系統,其中商品庫存資料表如下:

CREATE TABLE `items` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(16)),
  `remaining` INT NOT NULL DEFAULT 0,
  `is_enabled` INT NOT NULL DEFAULT 0
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;

插入幾筆水果庫存的測試資料:

INSERT INTO `items`
  (`name`, `remaining`, `is_enabled`)
VALUES
  ('apple', 20, 1),
  ('banana', 10, 0),
  ('cherry', 0, 0),
  ('guava', 0, 0),
  ('peach', 30, 1);

偷看一下資料表:

mysql> SELECT * FROM items;
+----+--------+-----------+------------+
| id | name   | remaining | is_enabled |
+----+--------+-----------+------------+
|  1 | apple  |        20 |          1 |
|  2 | banana |        10 |          0 |
|  3 | cherry |         0 |          0 |
|  4 | guava  |         0 |          0 |
|  5 | peach  |        30 |          1 |
+----+--------+-----------+------------+
5 rows in set (0.00 sec)

某天,老闆在貨車抵達門口進完貨後,走回自己辦公桌的電腦前面,想看看目前上架的水果狀態。

(水果行老闆為什麼會SQL不是本篇重點)

select…for update再insert造成deadlock的陷阱

SQL資料庫

最近side project在寫購票系統,因而踩中了這個坑。這個坑其實對InnoDB的鎖的觀念不熟悉的人會很難發現造成deadlock的原因,因此想特別寫一篇記錄一下,幫助也在開發類似功能的大家少走一點冤望路。

本篇的講解順序會先帶大家重現我之前引發Deadlock的購票功能邏輯,再來說明資料表使用InnoDB引擎下產生如此Deadlock的原因。

 

環境

  • 本篇範例使用的DB是 MariaDB 10.3.7 (開源版的MySQL)
  • 資料表採用預設的 InnoDB 引擎,隔離層級也是預設的 Repeatable Read
  • 範例資料表ticket的schema如下:
CREATE TABLE `ticket` (
  `id`      int(11) NOT NULL AUTO_INCREMENT,
  `show_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `ticket_show_id_IDX` (`show_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4

id是主鍵(PK)、show_id為了之後查詢上的需要,有建非唯一索引(non-unique index)、user_id理論上是外鍵(FK),但這個欄位不是本篇的重點所以忽略他。