mysql中的LOCK TABLES和UNLOCK TABLES
阿新 • • 發佈:2020-12-17
LOCK TABLES
tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...
UNLOCK TABLES
LOCK TABLES可以鎖定用於當前執行緒的表。如果表被其它執行緒鎖定,則造成堵塞,直到可以獲取所有鎖定為止。UNLOCK TABLES可以釋放被當前執行緒保持的任何鎖定。當執行緒釋出另一個LOCK TABLES時,或當與伺服器的連線被關閉時,所有由當前執行緒鎖定的表被隱含地解鎖。
表鎖定只用於防止其它客戶端進行不正當地讀取和寫入。保持鎖定(即使是讀取鎖定)的客戶端可以進行表層級的操作,比如DROP TABLE。
注意,下面是對事務表使用LOCK TABLES的說明:
· 在嘗試鎖定表之前,LOCK TABLES不是事務安全型的,會隱含地提交所有活性事務。同時,開始一項事務(例如,使用START TRANSACTION),會隱含地執行UNLOCK TABLES
· 對事務表(如InnoDB)使用LOCK TABLES的正確方法是,設定AUTOCOMMIT=0並且不能呼叫UNLOCK TABLES,直到您明確地提交事務為止。當您呼叫LOCK TABLES時,InnoDB會內部地取其自己的表鎖定,MySQL取其自己的表鎖定。InnoDB在下一個提交時釋放其表鎖定,但是,對於MySQL,要釋放表鎖定,您必須呼叫UNLOCK TABLES。您不應該讓AUTOCOMMIT=1,因為那樣的話,InnoDB會在呼叫LOCK TABLES之後立刻釋放表鎖定,並且很容易形成死鎖定。注意,如果AUTOCOMMIT=1,我們根本不能獲取InnoDB表鎖定,這樣就可以幫助舊的應用軟體避免不必要的死鎖定。
· ROLLBACK不會釋放MySQL的非事務表鎖定。
要使用LOCK TABLES,您必須擁有相關表的LOCK TABLES許可權和SELECT許可權。
使用LOCK TABLES的主要原因是仿效事務,或在更新表時加快速度。這將在後面進行更詳細的解釋。
如果一個執行緒獲得對一個表地READ鎖定,該執行緒(和所有其它執行緒)只能從該表中讀取。如果一個執行緒獲得對一個表的WRITE鎖定,只有保持鎖定的執行緒可以對錶進行寫入。其它的執行緒被阻止,直到鎖定被釋放時為止。
READ LOCAL和READ之間的區別是,READ LOCAL允許在鎖定被保持時,執行非衝突性INSERT語句(同時插入)。但是,如果您正打算在MySQL外面操作資料庫檔案,同時您保持鎖定,則不能使用READ LOCAL。對於InnoDB表,READ LOCAL與READ相同。
當您使用LOCK TABLES時,您必須鎖定您打算在查詢中使用的所有的表。雖然使用LOCK TABLES語句獲得的鎖定仍然有效,但是您不能訪問沒有被此語句鎖定的任何的表。同時,您不能在一次查詢中多次使用一個已鎖定的表——使用別名代替,在此情況下,您必須分別獲得對每個別名的鎖定。
mysql> LOCK TABLE t WRITE, t AS t1 WRITE;
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;
如果您的查詢使用一個別名引用一個表,那麼您必須使用同樣的別名鎖定該表。如果沒有指定別名,則不會鎖定該表。
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;
WRITE鎖定通常比READ鎖定擁有更高的優先權,以確保更新被儘快地處理。這意味著,如果一個執行緒獲得了一個READ鎖定,則另一個執行緒會申請一個WRITE鎖定,後續的READ鎖定申請會等待,直到WRITE執行緒獲得鎖定並釋放鎖定。您可以使用LOW_PRIORITY WRITE鎖定來允許其它執行緒在該執行緒正在等待WRITE鎖定時獲得READ鎖定。只有當您確定最終將有一個時機,此時沒有執行緒擁有READ鎖定時,您才應該使用LOW_PRIORITY WRITE鎖定。
LOCK TABLES按照如下方式執行:
1. 按照內部定義的順序,對所有要被鎖定的表進行分類。從使用者的角度,此順序是未經定義的。
2. 如果使用一個讀取和一個寫入鎖定對一個表進行鎖定,則把寫入鎖定放在讀取鎖定之前。
3. 一次鎖定一個表,直到執行緒得到所有鎖定為止。
該規則確保表鎖定不會出現死鎖定。但是,對於該規則,您需要注意其它的事情:
如果您正在對一個表使用一個LOW_PRIORITY WRITE鎖定,這隻意味著,MySQL等待特定的鎖定,直到沒有申請READ鎖定的執行緒時為止。當執行緒已經獲得WRITE鎖定,並正在等待得到鎖定表清單中的用於下一個表的鎖定時,所有其它執行緒會等待WRITE鎖定被釋放。如果這成為對於應用程式的嚴重的問題,則您應該考慮把部分錶轉化為事務安全型表。
您可以安全地使用KILL來結束一個正在等待表鎖定的執行緒。
注意,您不能使用INSERT DELAYED鎖定任何您正在使用的表,因為,在這種情況下,INSERT由另一個執行緒執行。
通常,您不需要鎖定表,因為所有的單個UPDATE語句都是原子性的;沒有其它的執行緒可以干擾任何其它當前正在執行的SQL語句。但是,在幾種情況下,鎖定表會有好處:
· 如果您正在對一組MyISAM表執行許多操作,鎖定您正在使用的表,可以快很多。鎖定MyISAM表可以加快插入、更新或刪除的速度。不利方面是,沒有執行緒可以更新一個用READ鎖定的表(包括保持鎖定的表),也沒有執行緒可以訪問用WRITE鎖定的表(除了保持鎖定的表以外)。
有些MyISAM操作在LOCK TABLES之下更快的原因是,MySQL不會清空用於已鎖定表的關鍵快取,直到UNLOCK TABLE被呼叫為止。通常,關鍵快取在每個SQL語句之後被清空。
· 如果您正在使用MySQL中的一個不支援事務的儲存引擎,則如果您想要確定在SELECT和UPDATE之間沒有其它執行緒,您必須使用LOCK TABLES。本處所示的例子要求LOCK TABLES,以便安全地執行:
· mysql> LOCK TABLES trans READ, customer WRITE;
· mysql> SELECT SUM(value) FROM trans WHERE customer_id=some_id;
· mysql> UPDATE customer
· -> SET total_value=sum_from_previous_statement
· -> WHERE customer_id=some_id;
· mysql> 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(),以加快速度。
要了解更多有關鎖定規則的說明
您可以使用FLUSH TABLES WITH READ LOCK語句鎖定位於所有帶有讀取鎖定的資料庫中的所有表。如果您有一個可以及時拍攝快照的檔案系統,比如Veritas,這是獲得備份的一個非常方便的方式。
註釋:如果您對一個已鎖定的表使用ALTER TABLE,該表可能會解鎖