MySQL表鎖定

在本教學中,您將學習如何使用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提供兩種鎖類型:READWRITE。 我們將在下一節詳細介紹這兩種鎖類型。

要釋放表的鎖,請使用以下語句:

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表上獲取了一個READ鎖,並且尚未釋放。

可以使用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

在本教學中,我們向您展示了如何鎖定和解鎖:READWRITE操作,以便在會話之間配合表訪問。


上一篇: MySQL刪除表數據 下一篇: MySQL創建與刪除資料庫