服務器級別的鎖
- 表鎖
表可以被顯示的讀鎖和寫鎖鎖定,查詢過程中也有隱式的鎖。在MySQL會話中使用LOCK TABLES命令可以顯示加鎖,當一個線程持有鎖后,其他線程會等待阻塞,這時使用的是MySQL服務器中的鎖,而不是存儲引擎的:
mysql> lock tables film read; #第一個連接
Query OK, 0 rows affected (0.00 sec)
mysql> lock tables film write; #第二個連接會掛起等待不會立即完成
Query OK, 0 rows affected (1 min 49.04 sec)
mysql> show processlist\G #顯示第二個連接id=40的線程是waiting狀態
*************************** 1. row ***************************
Id: 39
User: root
Host: localhost
db: sakila
Command: Query
Time: 0
State: init
Info: show processlist
*************************** 2. row ***************************
Id: 40
User: root
Host: localhost
db: sakila
Command: Query
Time: 103
State: Waiting for table metadata lock
Info: lock tables film write
2 rows in set (0.00 sec)
顯示鎖并不是只阻塞顯示鎖,服務器在查詢的時候有時會隱式地鎖住表,這時候嘗試LOCK 也會阻塞,因為此時表已經隱式地加鎖了:
mysql> select sleep(30) from film limit 1; #sleep 休眠
mysql> show processlist \G
*************************** 1. row ***************************
Id: 40
User: root
Host: localhost
db: sakila
Command: Query
Time: 0
State: init
Info: show processlist
*************************** 2. row ***************************
Id: 41
User: root
Host: localhost
db: sakila
Command: Query
Time: 17
State: Waiting for table metadata lock
Info: select sleep(30) from film limit 1
2 rows in set (0.00 sec)
- 全局鎖
通過 FLUSH TABLES WITH READ LOCK 或者設置 read_only=1來獲取單個全局鎖。它與任何表鎖沖突。這也是MySQL服務器實現的一個鎖,下面是一個全局讀鎖的實例:
mysql> flush table with read lock; #獲取一個全局讀鎖
Query OK, 0 rows affected (0.00 sec)
mysql> lock tables film write; #嘗試顯示上鎖
mysql> show processlist \G #state可以看到正在等待全局讀鎖
*************************** 1. row ***************************
Id: 40
User: root
Host: localhost
db: sakila
Command: Query
Time: 15
State: Waiting for global read lock
Info: lock tables film write
*************************** 2. row ***************************
Id: 43
User: root
Host: localhost
db: sakila
Command: Query
Time: 0
State: init
Info: show processlist
2 rows in set (0.00 sec)
- 命名鎖
命名鎖是表鎖的一種,服務器在重命名或創建一個表時創建,與普通的表鎖相沖突,無論是顯示還是隱式。
- 用戶鎖
在服務器中實現的一種鎖,一個基本的命名互斥量。創建鎖時需要制定鎖的字符串和超時秒數,如下面實例30秒超時的用戶鎖,其中一個線程創建用戶鎖后,另一個線程創建同名用戶鎖會阻塞30秒:
mysql> select get_lock('mylock',30);
+-----------------------+
| get_lock('mylock',30) |
+-----------------------+
| 1 |
+-----------------------+
1 row in set (0.00 sec)
mysql> select get_lock('mylock',30);
+-----------------------+
| get_lock('mylock',30) |
+-----------------------+
| 0 |
+-----------------------+
1 row in set (30.01 sec)
mysql> show processlist\G
*************************** 1. row ***************************
Id: 40
User: root
Host: localhost
db: sakila
Command: Query
Time: 5
State: User lock
Info: select get_lock('mylock',30)
*************************** 2. row ***************************
Id: 43
User: root
Host: localhost
db: sakila
Command: Query
Time: 0
State: init
Info: show processlist
2 rows in set (0.00 sec)
InnoDB中的鎖
InnoDB在show innodb status 中輸出一些鎖信息。如下開啟一個事務,并顯示加上寫鎖,此時另一個會話執行相同操作會阻塞:
mysql> begin; #開啟一個事務 加上行級排它鎖
Query OK, 0 rows affected (0.00 sec)
mysql> select film_id from film limit 1 for update;
mysql> begin; #此時另一個事務會超時等待,只有前一個事務commit提交釋放鎖才能執行成功
Query OK, 0 rows affected (0.00 sec)
mysql> select film_id from film limit 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
使用information_schema表
這里可以看到InnoDB的事務和鎖。