MySQL
建立MySQL
Container 與 simple_bank
database.
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=secret --network bank-network -d mysql:8
docker exec -it mysql mysql -u root -psecret -e "CREATE DATABASE simple_bank;"
Connect to MySQL and access simple_bank
database
docker exec -it mysql mysql -uroot -psecret simple_bank
Get current isolation level in MySQL
Repeatable Read
**select @@transaction_isolation;
這個SQL指令時,所獲得的隔離級別是針對當前的console session Level**Repeatable Read
**mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
select
@@global.transaction_isolation
這個SQL指令,用於查詢MySQL伺服器的全局隔離級別設定所獲得的隔離級別**Repeatable Read
**mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
1 row in set (0.00 sec)
Change isolation level in MySQL (current session
)
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立accounts
、entries
、transfers
Table
CREATE TABLE `accounts` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`owner` VARCHAR(255) NOT NULL,
`balance` BIGINT NOT NULL,
`currency` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `entries` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`account_id` BIGINT NOT NULL,
`amount` BIGINT NOT NULL COMMENT 'can be negative or positive',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `transfers` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`from_account_id` BIGINT NOT NULL,
`to_account_id` BIGINT NOT NULL,
`amount` BIGINT NOT NULL COMMENT 'must be positive',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX `accounts_index_0` ON `accounts` (`owner`);
CREATE UNIQUE INDEX `accounts_index_1` ON `accounts` (`owner`, `currency`);
CREATE INDEX `entries_index_2` ON `entries` (`account_id`);
CREATE INDEX `transfers_index_3` ON `transfers` (`from_account_id`);
CREATE INDEX `transfers_index_4` ON `transfers` (`to_account_id`);
CREATE INDEX `transfers_index_5` ON `transfers` (`from_account_id`, `to_account_id`);
ALTER TABLE `entries` ADD FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`);
ALTER TABLE `transfers` ADD FOREIGN KEY (`from_account_id`) REFERENCES `accounts` (`id`);
ALTER TABLE `transfers` ADD FOREIGN KEY (`to_account_id`) REFERENCES `accounts` (`id`);
插入三筆Account的資訊進到accounts
INSERT INTO `accounts` (`owner`, `balance`, `currency`) VALUES
('one', 100, 'USD'),
('two', 100, 'USD'),
('three', 100, 'USD');
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.01 sec)
Read uncommitted
isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Read Uncommitted
**
-- Tx1:
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1
中對accounts
table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
在Tx2
中對accounts
table進行id=1的 query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1
中對Account 1的balance減去10
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
最後我們在Tx1
與 Tx2
再次對Account 1進行Query
Tx1
尚未committed
但是Tx2
已經能看到Account 1的更新了dirty-read
並且只會發生在read-uncommitted
isolation level-- Tx1
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
Read committed
isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Read Committed
**
-- Tx1:
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1
中對accounts
table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
在Tx2
中對accounts
table進行id=1的 query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1
中對Account 1的balance減去10
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
我們在Tx2
再次對Account 1進行Query
Tx1
尚未committed
,所以Tx2
的Account 1 Query 依舊是90read-committed
可以避免dirty read
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
How about non-repeatable
and phantom read
?
non-repeatable
:
-- Tx2:
mysql> select * from accounts where balance >= 90;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
-- Tx1:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
再回到Tx2 對Account 1 進行查詢,可以發現和之前所獲得Value(90)不相同,這就被稱為non-repeatable
(請對照 “我們在Tx2
再次對Account 1進行Query”)
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
phantom read
同樣我們在Tx2
進行同一個Query取得balance 90以上的Accounts,可以發現相同的Query但是Return 的Record 數量不相同,這就被稱為phantom read
mysql> select * from accounts where balance >= 90;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
2 rows in set (0.00 sec)
所以read-committed
只能避免dirty read
無法避開non-repeatable
、phantom read
Repeatable
read isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable
**
-- Tx1:
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1
中對accounts
table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
在Tx2
中對accounts
table進行id=1的查詢與balance ≥80 的Query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> select * from accounts where balance >= 80;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
這時候在Tx1
中對Account 1的balance減去10
read committed
」中已經阻止了dirty read
, 所以就不需要再驗證-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
我們在Tx2
再次對Account 1進行查詢,來確認是否可以獲得Tx1
所更新的balance
Tx1
已經將其更改為70並且成功commit
。repeatable-read
isolation level 確保所有的讀取查詢都是repeatable
,這意味著,它總是返回相同的結果,即使其他已提交的交易進行了更改 (避免non-repeatable
)。phantom read
)。-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> select * from accounts where balance >= 80;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
如果我們在Tx2
對Account 1 的balance那結果會是如何?
rollback
這個交易,並嘗試進入最高隔離級別,看看這個問題是否可以被防止。mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
MySQL使用locking mechanism
來避免**Serialization anomaly
**
建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable
**
-- Tx1:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1
中對accounts
table進行select query,並進行Sum
和Insert
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 260 |
+--------------+
1 row in set (0.00 sec)
mysql> insert into accounts (owner, balance, currency) values ('sum', 260, 'USD');
Query OK, 1 row affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (0.00 sec)
在Tx2
中對accounts
table進行Select Query
-- Tx2
mysql> select * from accounts;
會發現這個查詢被blocked了,它需要等待Tx1
釋放lock
後才能繼續。
--Tx1
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
--Tx2
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (1.79 sec)
這時候可以再對Tx2
中對accounts
table進行select query,並進行Sum
和Insert
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 520 |
+--------------+
1 row in set (0.00 sec)
mysql> insert into accounts (owner, balance, currency) values ('sum', 520, 'USD');
Query OK, 1 row affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
| 5 | sum | 520 | USD | 2023-08-23 13:36:59 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.01 sec)
There’s no duplicate sum records. So MySQL has also successfully prevented the serialization anomaly
with its locking mechanism
.
接下來我現在我想在這兩個交易中嘗試不同的查詢順序
-- Tx1:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 1040 |
+--------------+
1 row in set (0.00 sec)
--Tx2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 1040 |
+--------------+
1 row in set (0.00 sec)
這次兩個交易都有相同的總和,為1040美元, 讓我們在Tx1
中插入一個帶有此總和的新帳戶
Tx2
持有一個share lock
,阻止其他交易進行更新。-- Tx1:
mysql> insert into accounts (owner, balance, currency) values ('sum', 1040, 'USD');
_
現在如果我們嘗試在Tx2
中插入一個新的總和帳戶
Tx1 和 Tx2
在互相等待-- Tx2:
mysql> insert into accounts (owner, balance, currency) values ('sum', 1040, 'USD');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
所以在Tx1
進行Commit後就可以正常運作
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
| 6 | sum | 1040 | USD | 2020-09-15 14:41:26 |
+----+-------+---------+----------+---------------------+
6 rows in set (0.00 sec)
The database stays consistent with no serialization anomaly
.
建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable
**
-- Tx1:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1
中對accounts
table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
在Tx2
中對accounts
table進行id=1的查詢
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2020-09-06 15:09:38 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1
中對Account 1的balance減去10
blocked
Tx2
中的Select查詢正在阻擋Tx1
中的這個更新查詢。-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
lock wait timeout error
serializable
isolation level時要確保實現transaction retry strategy 避免timeout
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
再來Restart Tx1
並進行Select Query 和Account 1的Update
--Tx1
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.01 sec)
mysql> update accounts set balance = balance - 10 where id = 1;
我們再回到Tx2
對Account 1 進行Update
DeadLock
,因為Tx2
也需要等待Tx1
的Select Query Locklock wait timeout
外,您還需要注意可能的DeadLock
情況--Tx2
mysql> update accounts set balance = balance - 10 where id = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
--Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (48.33 sec)
Rows matched: 1 Changed: 1 Warnings: 0
讓我們對Tx1
和 Tx2
進行重啟,並對Account 1 進行Select
-- Tx1:
mysql> rollback;
Query OK, 0 rows affected (0.03 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> rollback;
Query OK, 0 rows affected (0.03 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
讓我們在Tx1
更新Account 1 的Balance ,並且在 Tx2
進行commit
-- Tx1:
mysql> update accounts set balance = balance - 10 where id = 1;
-- Tx2:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
### After Commit
-- Tx1:
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (5.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)