1. 程式人生 > MYSQL進階教學 ><p>MySQL 伺服器級別的鎖等待</p>

<p>MySQL 伺服器級別的鎖等待</p>

使用鎖來控制資源共享的應用系統,如何處理鎖的競爭問題是個頭疼事。MySQL 有兩個級別的鎖等待,伺服器級別和儲存引擎級別,本節重點介紹伺服器級別的鎖等待。

1. 表鎖

表鎖可以是顯式的,也可以是隱式的。

1.1 顯式鎖

通過 lock tables和unlock tables 可以控制顯式鎖。在 MySQL 會話中執行 lock tables 命令,在表customer上會獲得一個顯式鎖。

mysql> lock tables customer read;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一個會話中,對錶 customer 執行 lock tables 命令,查詢會掛起。

mysql> lock tables customer write;

在第一個會話中執行 show processlist 檢視執行緒狀態,可以看到執行緒 13239868 的狀態為 Waiting for table metadata lock。在 MySQL 中,當一個執行緒持有該鎖後,其他執行緒只能不斷嘗試獲取。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13239801
   User: root
   Host: localhost
     db
: tempdb Command: Query Time: 0 State: starting Info: show processlist *************************** 2. row *************************** Id: 13239868 User: root Host: localhost db: tempdb Command: Query Time: 12 State: Waiting for table metadata lock Info: lock tables customer write
2 rows in set (0.00 sec)

1.2 隱式鎖

除了顯式鎖會阻塞這樣的操作,MySQL 在查詢過程中也會隱式地鎖住表。通過 sleep() 函式可以實現長時間的查詢,然後 MySQL 會產生一個隱式鎖。

在 MySQL 會話中執行 sleep(30),在表 customer上 會獲得一個隱式鎖。

mysql> select sleep(30) from customer;

在 MySQL 另一個會話中,對錶 customer 執行 lock tables 命令,查詢會掛起。

mysql> lock tables customer write;

在第三個會話中執行 show processlist 檢視執行緒狀態,可以看到執行緒 13244135 的狀態為 Waiting for table metadata lock。select 查詢的隱式鎖阻塞了 lock tables 中所請求的顯式寫鎖。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13244112
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 6
  State: User sleep
   Info: select sleep(30) from customer
*************************** 2. row ***************************
     Id: 13244135
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 2
  State: Waiting for table metadata lock
   Info: lock tables customer write

2. 全域性鎖

MySQL 伺服器可以支援全域性讀鎖,可以通過 flush tables with read lock 或設定 read_only=1 來實現,全域性鎖與任何表鎖都衝突。

在 MySQL會 話中執行 flush tables 命令,獲得全域性讀鎖。

mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一個會話中,對錶 customer 執行 lock tables 命令,查詢會掛起。

mysql> lock tables customer write;

在第一個會話中執行 show processlist 檢視執行緒狀態,可以看到執行緒 13283816 的狀態為 Waiting for global read lock。這是一個全域性讀鎖,而不是表級別鎖。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13283789
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 2. row ***************************
     Id: 13283816
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 10
  State: Waiting for global read lock
   Info: lock tables customer write
2 rows in set (0.00 sec)

3. 命名鎖

命名鎖是一種表級別鎖,它是 MySQL 伺服器在重新命名或刪除表時建立。命名鎖與普通的表鎖衝突,無論是顯式的還是隱式的表鎖。

在 MySQL會 話中執行 lock table s命令,在表 customer上 獲得一個顯式鎖。

mysql> lock tables customer read;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一個會話中,對錶 customer 執行 rename table 命令,此時會話會掛起,會話狀態為Waiting for table metadata lock:

mysql> rename table customer to customer_1;

mysql> show processlist\G
...
*************************** 2. row ***************************
     Id: 51
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 128
  State: Waiting for table metadata lock
   Info: rename table customer to customer_1

4. 使用者鎖

MySQL 伺服器還可以實現使用者鎖,這種鎖需指定名稱字串,以及等待超時時間(單位秒)。

在 MySQL 會話中執行 get_lock 命令,成功執行並持有一把鎖。

mysql> select get_lock('user_1',20);
+------------------------+
| get_lock('user_1',20) |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)

在 MySQL 另一個會話中,也執行 get_lock 命令,嘗試鎖相同的字串,此時會話會掛起,會話狀態為User lock。

mysql> select get_lock('user_1',20);
+------------------------+
| get_lock('user_1',20) |
+------------------------+
|                      1 |
+------------------------+

mysql> show processlist\G
...
*************************** 2. row ***************************
     Id: 51
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 3
  State: User lock
   Info: select get_lock('user_1',20)

5. 小結

本小節介紹了伺服器級別的鎖等待:表鎖、全域性鎖、命名鎖、使用者鎖。

表鎖可以是顯式的,也可以是隱式的。顯式鎖通過 lock tables 和 unlock tables 進行控制,隱式鎖在查詢過程中產生。全域性鎖可以通過 flush tables with read lock 或設定 read_only=1 來 實現,它與任何表鎖都衝突。