在本教學中,您將學習如何使用MySQL鎖來協調會話之間的表訪問。
MySQL允許客戶端會話明確獲取表鎖,以防止其他會話在特定時間段內訪問表。客戶端會話只能為自己獲取或釋放表鎖。它不能獲取或釋放其他會話的表鎖。
在詳細介紹之前,我們將創建一個名為sampledb
的示例資料庫,其中包含一個簡單的tbl
表來模擬練習表鎖定語句。
CREATE DATABASE IF NOT EXISTS testdb;
USE testdb;
CREATE TABLE tbl (
id int(11) NOT NULL AUTO_INCREMENT,
col int(11) NOT NULL,
PRIMARY KEY (id)
);
LOCK和UNLOCK TABLES語法
獲取表的鎖的簡單形式如下:
LOCK TABLES table_name [READ | WRITE]
可將表的名稱放在LOCK TABLES
關鍵字後面,後跟一個鎖類型。 MySQL提供兩種鎖類型:READ
和WRITE
。 我們將在下一節詳細介紹這兩種鎖類型。
要釋放表的鎖,請使用以下語句:
UNLOCK TABLES;
表鎖定為READ
表的READ鎖具有以下功能:
- 同時可以通過多個會話獲取表的
READ
鎖。此外,其他會話可以從表中讀取數據,而無需獲取鎖定。 - 持有
READ
鎖的會話只能從表中讀取數據,但不能寫入。此外,其他會話在釋放READ
鎖之前無法將數據寫入表中。來自另一個會話的寫操作將被放入等待狀態,直到釋放READ
鎖。 - 如果會話正常或異常終止,MySQL將會隱式釋放所有鎖。這也與
WRITE
鎖相關。
下麵我們來看看在以下情況下READ
鎖如何工作。
首先,連接到testdb
資料庫。要查找當前的連接ID,請使用CONNECTION_ID()
函數,如下所示:
mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
| 6 |
+-----------------+
1 row in set
然後,在向tbl
表中插入一個新行。
INSERT INTO tbl(col) VALUES(10);
接下來,從上表tbl
中檢索所有行。
mysql> SELECT * FROM tbl;
+----+-----+
| id | col |
+----+-----+
| 1 | 10 |
+----+-----+
1 row in set
之後,要獲取鎖,可以使用LOCK TABLE
語句。
最後,在同一個會話中,如果您嘗試在tbl
表中插入一個新行,將收到一條錯誤消息。
mysql> LOCK TABLE tbl READ;
Query OK, 0 rows affected
mysql> INSERT INTO tbl(col) VALUES(11);
1099 - Table 'tbl' was locked with a READ lock and can't be updated
mysql>
所以一旦獲得了READ
鎖定,就不能在同一個會話中的表中寫入數據。讓我們從不同的會話中來查看READ
鎖。
首先,打開另一個終端並連接到資料庫testdb
,然後檢查連接ID:
mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
| 7 |
+-----------------+
1 row in set
然後,從tbl
檢索數據,如下所示 -
mysql> SELECT * FROM tbl;
+----+-----+
| id | col |
+----+-----+
| 1 | 10 |
+----+-----+
1 row in set
接下來,從第二個會話(會話ID為7
)插入一個新行到tbl
表中。
第二個會話的插入操作處於等待狀態,因為第一個會話已經在tbl
表上獲取了一個RE
AD鎖,並且尚未釋放。
可以使用SHOW PROCESSLIST
語句查看詳細資訊,如下所示 -
mysql> SHOW PROCESSLIST;
+----+------+-----------------+----------+---------+------+---------------------------------+---------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------------+----------+---------+------+---------------------------------+---------------------------------+
| 2 | root | localhost:51998 | NULL | Sleep | 474 | | NULL |
| 3 | root | localhost:51999 | zaixiandb | Sleep | 3633 | | NULL |
| 6 | root | localhost:52232 | testdb | Query | 0 | starting | SHOW PROCESSLIST |
| 7 | root | localhost:53642 | testdb | Query | 110 | Waiting for table metadata lock | INSERT INTO tbl(col) VALUES(20) |
+----+------+-----------------+----------+---------+------+---------------------------------+---------------------------------+
4 rows in set
之後,返回第一個會話並使用UNLOCK TABLES
語句來釋放鎖。從第一個會話釋放READ
鎖之後,在第二個會話中執行INSERT
操作。
最後,查看tbl
表中的數據,以查看第二個會話中的INSERT
操作是否真的執行。
mysql> SELECT * FROM tbl;
+----+-----+
| id | col |
+----+-----+
| 1 | 10 |
| 2 | 20 |
+----+-----+
2 rows in set
將MySQL表鎖定WRITE
表鎖為WRITE
具有以下功能:
- 只有擁有表鎖定的會話才能從表讀取和寫入數據。
- 在釋放
WRITE
鎖之前,其他會話不能從表中讀寫。
詳細瞭解WRITE
鎖的工作原理。
首先,從第一個會話獲取一個WRITE
鎖。
LOCK TABLE tbl WRITE;
然後,在tbl
表中插入一個新行。
INSERT INTO tbl(col) VALUES(11);
沒有問題,上面語句可能正常執行。接下來,從tbl
表讀取數據。
mysql> SELECT * FROM tbl;
+----+-----+
| id | col |
+----+-----+
| 1 | 10 |
| 2 | 20 |
| 3 | 11 |
+----+-----+
3 rows in set
之後,打開第二個連接到MySQL的會話,嘗試寫和讀數據:
MySQL將這些操作置於等待狀態。可以在第一個會話中,使用SHOW PROCESSLIST
語句來查看它。
mysql> SHOW PROCESSLIST;
+----+------+-----------------+----------+---------+-------+---------------------------------+---------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------------+----------+---------+-------+---------------------------------+---------------------------------+
| 2 | root | localhost:51998 | NULL | Sleep | 8477 | | NULL |
| 3 | root | localhost:51999 | zaixiandb | Sleep | 11636 | | NULL |
| 8 | root | localhost:54012 | testdb | Sleep | 119 | | NULL |
| 9 | root | localhost:54013 | testdb | Query | 0 | starting | SHOW PROCESSLIST |
| 10 | root | localhost:54016 | testdb | Query | 49 | Waiting for table metadata lock | INSERT INTO tbl(col) VALUES(21) |
+----+------+-----------------+----------+---------+-------+---------------------------------+---------------------------------+
5 rows in set
最後,從第一個會話釋放鎖。執行以下語句 -
UNLOCK TABLES;
執行上面語句後,將看到第二個會話中的所有待處理已經執行操作。
SELECT * FROM tbl;
Query OK, 1 row affected
+----+-----+
| id | col |
+----+-----+
| 1 | 10 |
| 2 | 20 |
| 3 | 11 |
| 4 | 21 |
+----+-----+
4 rows in set
在本教學中,我們向您展示了如何鎖定和解鎖:READ
和WRITE
操作,以便在會話之間配合表訪問。