LOCK {TABLE | TABLES}
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| WRITE
}
UNLOCK {TABLE | TABLES}
MySQL 允許客戶端工作階段明確取得資料表鎖定,以便與其他工作階段協同存取資料表,或者防止其他工作階段在某個工作階段需要獨佔存取資料表的期間修改資料表。一個工作階段只能為自己取得或釋放鎖定。一個工作階段無法為另一個工作階段取得鎖定,也無法釋放另一個工作階段持有的鎖定。
鎖定可用於模擬交易或在更新資料表時獲得更高的速度。這在資料表鎖定的限制和條件中有更詳細的解釋。
LOCK TABLES
明確地為目前用戶端工作階段取得資料表鎖定。可以為基本表格或檢視表取得資料表鎖定。您必須擁有 LOCK TABLES
權限,以及每個要鎖定的物件的 SELECT
權限。
對於視圖鎖定,LOCK TABLES
會將視圖中使用的所有基本資料表加入要鎖定的資料表集合,並自動鎖定它們。對於任何被鎖定的視圖所使用的基礎資料表,LOCK TABLES
會檢查視圖定義者(對於 SQL SECURITY DEFINER
視圖)或呼叫者(對於所有視圖)是否對這些資料表擁有適當的權限。
如果您使用 LOCK TABLES
明確地鎖定資料表,則觸發程序中使用的任何資料表也會隱含地被鎖定,如 LOCK TABLES 和觸發程序中所述。
如果您使用 LOCK TABLES
明確地鎖定資料表,則以外鍵約束相關聯的任何資料表也會隱含地被開啟和鎖定。對於外鍵檢查,會在相關的資料表上取得共用唯讀鎖定(LOCK TABLES READ
)。對於級聯更新,會在操作中涉及的相關資料表上取得非共用寫入鎖定(LOCK TABLES WRITE
)。
UNLOCK TABLES
明確釋放目前會話所持有的任何資料表鎖定。LOCK TABLES
在取得新的鎖定之前,會隱含地釋放目前會話所持有的任何資料表鎖定。
UNLOCK TABLES
的另一個用途是釋放使用 FLUSH TABLES WITH READ LOCK
語句取得的全域讀取鎖定,這可讓您鎖定所有資料庫中的所有資料表。請參閱 第 15.7.8.3 節「FLUSH 語句」。(如果您擁有像 Veritas 這樣可以隨時間拍攝快照的檔案系統,這是一種非常方便的備份方法。)
LOCK TABLE
是 LOCK TABLES
的同義詞;UNLOCK TABLE
是 UNLOCK TABLES
的同義詞。
資料表鎖定僅保護其他會話不進行不適當的讀取或寫入。持有 WRITE
鎖定的會話可以執行資料表層級的操作,例如 DROP TABLE
或 TRUNCATE TABLE
。對於持有 READ
鎖定的會話,不允許執行 DROP TABLE
和 TRUNCATE TABLE
操作。
以下討論僅適用於非 TEMPORARY
資料表。對於 TEMPORARY
資料表,允許(但會被忽略)使用 LOCK TABLES
。建立它的會話可以自由存取該資料表,無論其他鎖定是否有效。由於沒有其他會話可以看到該資料表,因此不需要任何鎖定。
若要在目前會話中取得資料表鎖定,請使用 LOCK TABLES
語句,該語句會取得中繼資料鎖定(請參閱 第 10.11.4 節「中繼資料鎖定」)。
可以使用以下鎖定類型
READ [LOCAL]
鎖定
持有鎖定的會話可以讀取資料表(但不能寫入)。
多個會話可以同時取得資料表的
READ
鎖定。其他會話可以讀取資料表,而無需明確取得
READ
鎖定。LOCAL
修飾詞可讓其他會話在鎖定保持期間執行不衝突的INSERT
語句(並行插入)。(請參閱 第 10.11.3 節「並行插入」。)但是,如果您在持有鎖定的同時使用伺服器外部的程序來操作資料庫,則不能使用READ LOCAL
。對於InnoDB
資料表,READ LOCAL
與READ
相同。
WRITE
鎖定
持有鎖定的會話可以讀取和寫入資料表。
只有持有鎖定的會話才能存取資料表。在鎖定釋放之前,沒有其他會話可以存取它。
在持有
WRITE
鎖定時,其他會話對資料表的鎖定請求會被封鎖。
WRITE
鎖定的優先順序通常高於 READ
鎖定,以確保盡快處理更新。這表示如果一個會話取得 READ
鎖定,然後另一個會話請求 WRITE
鎖定,則後續的 READ
鎖定請求會等待,直到請求 WRITE
鎖定的會話取得鎖定並釋放它。(對於系統變數 max_write_lock_count
的較小值,可能會發生此原則的例外情況;請參閱 第 10.11.4 節「中繼資料鎖定」。)
如果 LOCK TABLES
語句由於其他會話對任何資料表持有的鎖定而必須等待,則它會被封鎖,直到可以取得所有鎖定。
需要鎖定的會話必須在單一 LOCK TABLES
語句中取得它需要的所有鎖定。當這樣取得的鎖定被持有時,會話只能存取鎖定的資料表。例如,在以下語句序列中,嘗試存取 t2
時會發生錯誤,因為它未在 LOCK TABLES
語句中鎖定
mysql> LOCK TABLES t1 READ;
mysql> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
| 3 |
+----------+
mysql> SELECT COUNT(*) FROM t2;
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
INFORMATION_SCHEMA
資料庫中的資料表是一個例外。即使會話持有使用 LOCK TABLES
取得的資料表鎖定,也可以在不顯式鎖定的情況下存取它們。
您不能在單一查詢中使用相同的名稱多次引用鎖定的資料表。請改用別名,並為資料表和每個別名取得單獨的鎖定
mysql> LOCK TABLE t WRITE, t AS t1 READ;
mysql> INSERT INTO t SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> INSERT INTO t SELECT * FROM t AS t1;
第一個 INSERT
會發生錯誤,因為對於鎖定的資料表有兩個相同的名稱引用。第二個 INSERT
成功,因為對資料表的引用使用不同的名稱。
如果您的語句透過別名引用資料表,則必須使用相同的別名鎖定資料表。不指定別名就鎖定資料表是無效的
mysql> LOCK TABLE t READ;
mysql> SELECT * FROM t AS myalias;
ERROR 1100: Table 'myalias' was not locked with LOCK TABLES
反之,如果您使用別名鎖定資料表,則必須在您的語句中使用該別名來引用它
mysql> LOCK TABLE t AS myalias READ;
mysql> SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> SELECT * FROM t AS myalias;
當會話持有的資料表鎖定被釋放時,它們會同時被全部釋放。會話可以明確地釋放其鎖定,或者鎖定可能會在某些條件下隱含地釋放。
會話可以使用
UNLOCK TABLES
明確地釋放其鎖定。如果會話發出
LOCK TABLES
語句以取得鎖定,同時已持有鎖定,則其現有的鎖定會在授予新鎖定之前隱含地釋放。如果會話開始交易(例如,使用
START TRANSACTION
),則會執行隱含的UNLOCK TABLES
,這會導致現有的鎖定被釋放。(如需有關資料表鎖定和交易之間互動的其他資訊,請參閱 資料表鎖定和交易的互動。)
如果用戶端會話的連線終止(無論是正常還是異常終止),伺服器都會隱含地釋放會話持有的所有資料表鎖定(交易型和非交易型)。如果用戶端重新連線,則鎖定不再有效。此外,如果用戶端有作用中的交易,則伺服器會在斷線時復原交易,並且如果發生重新連線,則新的會話會以啟用自動提交開始。因此,用戶端可能會希望停用自動重新連線。在自動重新連線有效的情況下,如果發生重新連線,則不會通知用戶端,但任何資料表鎖定或目前交易都會遺失。在停用自動重新連線的情況下,如果連線中斷,則發出的下一個語句會發生錯誤。用戶端可以偵測到錯誤並採取適當的動作,例如重新取得鎖定或重做交易。請參閱 自動重新連線控制。
如果您對鎖定的資料表使用 ALTER TABLE
,則它可能會被解除鎖定。例如,如果您嘗試第二次執行 ALTER TABLE
操作,則結果可能是錯誤 Table '
。若要處理此問題,請在第二次變更之前再次鎖定資料表。另請參閱 第 B.3.6.1 節「ALTER TABLE 的問題」。tbl_name
' was not locked with LOCK TABLES
LOCK TABLES
和 UNLOCK TABLES
會以如下方式與交易的使用互動
LOCK TABLES
不是交易安全的,並且在嘗試鎖定資料表之前會隱含地提交任何作用中的交易。UNLOCK TABLES
會隱含地提交任何作用中的交易,但前提是必須先使用LOCK TABLES
來取得資料表鎖定。例如,在以下語句集中,UNLOCK TABLES
會釋放全域讀取鎖定,但不會提交交易,因為沒有任何資料表鎖定生效。FLUSH TABLES WITH READ LOCK; START TRANSACTION; SELECT ... ; UNLOCK TABLES;
開始交易(例如,使用
START TRANSACTION
)會隱含地提交任何目前的交易,並釋放現有的資料表鎖定。FLUSH TABLES WITH READ LOCK
會取得全域讀取鎖定,而不是資料表鎖定,因此它不受LOCK TABLES
和UNLOCK TABLES
在資料表鎖定和隱含提交方面的相同行為約束。例如,START TRANSACTION
不會釋放全域讀取鎖定。請參閱第 15.7.8.3 節「FLUSH 語句」。其他隱含地導致交易提交的語句不會釋放現有的資料表鎖定。如需此類語句的列表,請參閱第 15.3.3 節「導致隱含提交的語句」。
搭配使用交易資料表(例如
InnoDB
資料表)的正確方式是,先使用SET autocommit = 0
(而不是START TRANSACTION
)開始交易,然後使用LOCK TABLES
,並且直到您明確提交交易之前,都不要呼叫UNLOCK TABLES
。例如,如果您需要寫入資料表t1
並從資料表t2
讀取,您可以這樣做SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...; ... do something with tables t1 and t2 here ... COMMIT; UNLOCK TABLES;
當您呼叫
LOCK TABLES
時,InnoDB
會在內部取得自己的資料表鎖定,而 MySQL 會取得自己的資料表鎖定。InnoDB
會在下一次提交時釋放其內部資料表鎖定,但若要讓 MySQL 釋放其資料表鎖定,您必須呼叫UNLOCK TABLES
。您不應該設定autocommit = 1
,因為這樣InnoDB
會在呼叫LOCK TABLES
後立即釋放其內部資料表鎖定,而且非常容易發生死鎖。InnoDB
如果autocommit = 1
,根本不會取得內部資料表鎖定,以協助舊應用程式避免不必要的死鎖。ROLLBACK
不會釋放資料表鎖定。
如果您使用 LOCK TABLES
明確鎖定資料表,則在觸發程序中使用的任何資料表也會隱含地被鎖定。
鎖定會與使用
LOCK TABLES
語句明確取得的鎖定同時取得。在觸發程序中使用的資料表鎖定取決於該資料表是否僅用於讀取。如果是,則讀取鎖定就足夠了。否則,會使用寫入鎖定。
如果資料表使用
LOCK TABLES
明確鎖定為讀取,但因為它可能會在觸發程序內修改而需要鎖定為寫入,則會取得寫入鎖定,而不是讀取鎖定。(也就是說,由於資料表出現在觸發程序中而需要的隱含寫入鎖定,會導致針對該資料表的明確讀取鎖定要求轉換為寫入鎖定要求。)
假設您使用以下語句鎖定兩個資料表,t1
和 t2
LOCK TABLES t1 WRITE, t2 READ;
如果 t1
或 t2
有任何觸發程序,則觸發程序內使用的資料表也會被鎖定。假設 t1
定義了像這樣的觸發程序
CREATE TRIGGER t1_a_ins AFTER INSERT ON t1 FOR EACH ROW
BEGIN
UPDATE t4 SET count = count+1
WHERE id = NEW.id AND EXISTS (SELECT a FROM t3);
INSERT INTO t2 VALUES(1, 2);
END;
LOCK TABLES
語句的結果是,t1
和 t2
會因為它們出現在語句中而被鎖定,而 t3
和 t4
會因為它們在觸發程序內被使用而被鎖定。
t1
會根據WRITE
鎖定要求而鎖定為寫入。t2
會鎖定為寫入,即使要求是READ
鎖定。發生這種情況是因為t2
是在觸發程序內插入,因此READ
要求會轉換為WRITE
要求。t3
會鎖定為讀取,因為它只在觸發程序內被讀取。t4
會鎖定為寫入,因為它可能會在觸發程序內更新。
您可以安全地使用 KILL
來終止正在等待資料表鎖定的工作階段。請參閱第 15.7.8.4 節「KILL 語句」。
LOCK TABLES
和 UNLOCK TABLES
無法在儲存的程式碼內使用。
performance_schema
資料庫中的資料表無法使用 LOCK TABLES
鎖定,但 setup_
資料表除外。xxx
LOCK TABLES
產生的鎖定範圍為單一 MySQL 伺服器。它與 NDB Cluster 不相容,因為 NDB Cluster 無法在多個 mysqld 執行個體之間強制執行 SQL 層級的鎖定。您可以改為在 API 應用程式中強制執行鎖定。如需更多資訊,請參閱第 25.2.7.10 節「與多個 NDB Cluster 節點相關的限制」。
在 LOCK TABLES
語句生效時,禁止使用以下語句:CREATE TABLE
、CREATE TABLE ... LIKE
、CREATE VIEW
、DROP VIEW
以及儲存的函數和程序及事件的 DDL 語句。
對於某些作業,必須存取 mysql
資料庫中的系統資料表。例如,HELP
語句需要伺服器端說明資料表的內容,而 CONVERT_TZ()
可能需要讀取時區資料表。伺服器會在必要時隱含地鎖定系統資料表以進行讀取,因此您不需要明確地鎖定它們。這些資料表會被視為如上所述
mysql.help_category
mysql.help_keyword
mysql.help_relation
mysql.help_topic
mysql.time_zone
mysql.time_zone_leap_second
mysql.time_zone_name
mysql.time_zone_transition
mysql.time_zone_transition_type
如果您想要使用 LOCK TABLES
語句明確地在其中任何一個資料表上放置 WRITE
鎖定,則該資料表必須是唯一鎖定的資料表;沒有其他資料表可以使用相同的語句鎖定。
通常,您不需要鎖定資料表,因為所有單一 UPDATE
語句都是不可分割的;沒有其他工作階段可以干擾任何其他目前正在執行的 SQL 語句。但是,在某些情況下,鎖定資料表可能會提供優勢
如果您要在多組
MyISAM
資料表上執行許多作業,則鎖定您要使用的資料表會快得多。鎖定MyISAM
資料表會加快在其上進行插入、更新或刪除的速度,因為 MySQL 不會在呼叫UNLOCK TABLES
之前清除已鎖定資料表的金鑰快取。通常,會在每個 SQL 語句之後清除金鑰快取。鎖定資料表的缺點是,沒有工作階段可以更新
READ
鎖定的資料表(包括持有鎖定的資料表),而且除了持有鎖定的工作階段以外,沒有其他工作階段可以存取WRITE
鎖定的資料表。如果您使用資料表用於非交易儲存引擎,則如果您想要確保在
SELECT
和UPDATE
之間沒有其他工作階段修改資料表,則必須使用LOCK TABLES
。此處顯示的範例需要LOCK TABLES
才能安全執行LOCK TABLES trans READ, customer WRITE; SELECT SUM(value) FROM trans WHERE customer_id=some_id; UPDATE customer SET total_value=sum_from_previous_statement WHERE customer_id=some_id; UNLOCK TABLES;
如果沒有
LOCK TABLES
,則另一個工作階段可能會在執行SELECT
和UPDATE
語句之間在trans
資料表中插入新資料列。
您可以在許多情況下透過使用相對更新 (UPDATE customer SET
) 或 value
=value
+new_value
LAST_INSERT_ID()
函數來避免使用 LOCK TABLES
。
您也可以在某些情況下透過使用使用者層級諮詢鎖定函數 GET_LOCK()
和 RELEASE_LOCK()
來避免鎖定資料表。這些鎖定會儲存在伺服器中的雜湊表內,並使用 pthread_mutex_lock()
和 pthread_mutex_unlock()
來實作,以達到高速效果。請參閱第 14.14 節「鎖定函數」。
請參閱第 10.11.1 節「內部鎖定方法」,以瞭解更多關於鎖定政策的資訊。