轉自:https://mp.weixin.qq.com/s/SA7VSwBRvV1RAweCNs7DDA
MySQL 8.0 也支持 nowait, skip locked 語法了。在 09 年飛信項目中看到 SQL Server 的這個語法時內心充滿了羡慕。現在 MySQL8.0 也可以支持了,不過和其它 DB 實現的有點不一樣。我們下面一起看看 MySQL 8.0 對 nowait 和 skip locked 的使用。
了解原理
首先我們需要知道 MySQL 中 nowait, skip locked 是 select ... for update|share 鎖定讀的語法糖。而且只作用row lock 的鎖定。
- NOWAIT
該query立即執行,獲取不到鎖就返回失敗
- skip locked
該 query 立即執行,獲取不到鎖的跳過該行。結果中就沒有該行記錄了。
實戰測試
接下來,我們直接看例子:#初始化數據 session1
mysql> CREATE TABLE t (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;mysql> INSERT INTO t (i) VALUES(1),(2),(3);
mysql> SELECT * FROM t;
+---+
| i |
+---+
| 1 |
| 2 |
| 3 |
+---+
#初始化 3 條數據就行了,多了數不過來。#session1
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> select * from t where i=2 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
從上面可以看到 for update 執行需要等待鎖超時才能退出。接下來我們看一下 for update nowait 執行:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set.
從這里可以看到在 for update nowait 直接執行返回,從錯誤提示上可以出來因為設置了 nowait 因為獲取不到鎖直接退出了。# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+
Query 直接執行返回,但從結果上可以看出來少了一條記錄:2, 原因是 2 這條記錄正處在鎖 X 鎖狀態下,因為使用了 skip locked,直接跳過該條記錄。
MySQL 當前鎖情況
可以利用 sys.innodb_lock_waits 這個表查詢當前鎖等待,死鎖等情況。
mysql> select * from sys.innodb_lock_waits\G;
*************************** 1. row ***************************
wait_started: 2021-06-07 00:56:24
wait_age: 00:00:10
wait_age_secs: 10
locked_table: `wubx`.`t`
locked_table_schema: wubx
locked_table_name: t
locked_table_partition: NULL
locked_table_subpartition: NULL
locked_index: PRIMARY
locked_type: RECORD
waiting_trx_id: 1633
waiting_trx_started: 2021-06-07 00:56:24
waiting_trx_age: 00:00:10
waiting_trx_rows_locked: 1
waiting_trx_rows_modified: 0
waiting_pid: 345
waiting_query: select * from t where i=2 for update
waiting_lock_id: 139804827496688:2:4:3:139804742333008
waiting_lock_mode: X,REC_NOT_GAP
blocking_trx_id: 1630
blocking_pid: 344
blocking_query: NULL
blocking_lock_id: 139804827495832:2:4:3:139804742326848
blocking_lock_mode: X,REC_NOT_GAP
blocking_trx_started: 2021-06-07 00:54:21
blocking_trx_age: 00:02:13
blocking_trx_rows_locked: 1
blocking_trx_rows_modified: 0
sql_kill_blocking_query: KILL QUERY 344
sql_kill_blocking_connection: KILL 344
1 row in set (0.00 sec)
使用場景
nowait, skip locked 應該說在人為臨時統計時特別常用,但我們一般使用是加 for share 鎖,不是 for update。例如,在讀數據時嘗試 for share nowait,如果有獲取不到鎖直接退出,不要影響其它 SQL,再嘗試是不是可以用 for share skip locked 執行,獲取一個近似值。這個語法給后台人為的臨時統計,臨時數據抽樣增加了更多的靈活性。這方面你有什么好的用法,也歡迎在評論中給大家分享一下。
