本節討論內部鎖定;也就是說,MySQL 伺服器本身執行的鎖定,以管理多個工作階段對資料表內容的爭用。此類型的鎖定是內部的,因為它完全由伺服器執行,且不涉及其他程式。如需其他程式對 MySQL 檔案執行的鎖定,請參閱第 10.11.5 節,〈外部鎖定〉。
MySQL 對 InnoDB
資料表使用資料列層級鎖定,以支援多個工作階段的同時寫入存取,使其適用於多使用者、高度並行和 OLTP 應用程式。
為了避免在單一 InnoDB
資料表上執行多個並行寫入作業時發生死結,請在交易開始時取得必要的鎖定,針對預期修改的每一組資料列發出 SELECT ... FOR UPDATE
語句,即使資料變更語句稍後才在交易中出現。如果交易修改或鎖定多個資料表,請在每個交易中以相同的順序發出適用的語句。死結會影響效能,而不是表示嚴重的錯誤,因為 InnoDB
預設會自動偵測死結情況,並回滾受影響的其中一個交易。
在高並行系統上,當大量執行緒等待相同鎖定時,死結偵測可能會導致效能下降。有時,當發生死結時,停用死結偵測並依賴 innodb_lock_wait_timeout
設定進行交易回滾可能更有效率。可以使用 innodb_deadlock_detect
組態選項停用死結偵測。
資料列層級鎖定的優點
當不同工作階段存取不同資料列時,鎖定衝突較少。
回滾的變更較少。
可以長時間鎖定單一資料列。
MySQL 對 MyISAM
、MEMORY
和 MERGE
資料表使用資料表層級鎖定,一次只允許一個工作階段更新這些資料表。此鎖定層級使這些儲存引擎更適合唯讀、幾乎唯讀或單一使用者應用程式。
這些儲存引擎總是要求在查詢開始時一次取得所有需要的鎖定,並總是按相同順序鎖定資料表,以避免死結。折衷方案是,此策略會降低並行性;其他想要修改資料表的工作階段必須等到目前的資料變更語句完成。
資料表層級鎖定的優點
需要的記憶體相對較少(資料列鎖定需要每個資料列或鎖定資料列群組的記憶體)
當在資料表的大部分上使用時,速度很快,因為只涉及單一鎖定。
如果您經常對大部分資料執行
GROUP BY
作業,或必須經常掃描整個資料表,則速度很快。
MySQL 會依下列方式授與資料表寫入鎖定
如果資料表上沒有鎖定,則在其上放置寫入鎖定。
否則,將鎖定要求放入寫入鎖定佇列。
MySQL 會依下列方式授與資料表讀取鎖定
如果資料表上沒有寫入鎖定,則在其上放置讀取鎖定。
否則,將鎖定要求放入讀取鎖定佇列。
表格更新的優先級高於表格檢索。因此,當鎖定被釋放時,該鎖定會先提供給寫入鎖定佇列中的請求,然後再提供給讀取鎖定佇列中的請求。這確保了即使表格有大量的 SELECT
活動,表格的更新也不會「「餓死」」。然而,如果表格有許多更新,SELECT
語句會等到沒有更多更新為止。
有關變更讀取和寫入優先順序的資訊,請參閱第 10.11.2 節「表格鎖定問題」。
您可以藉由檢查 Table_locks_immediate
和 Table_locks_waited
狀態變數來分析系統上的表格鎖定爭用,這兩個變數分別表示表格鎖定請求可以立即授權的次數,以及必須等待的次數。
mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited | 15324 |
+-----------------------+---------+
Performance Schema 鎖定表格也提供鎖定資訊。請參閱第 29.12.13 節「Performance Schema 鎖定表格」。
MyISAM
儲存引擎支援並行插入,以減少給定表格的讀取器和寫入器之間的爭用:如果 MyISAM
表格的資料檔案中間沒有可用區塊,則行會永遠插入到資料檔案的末尾。在這種情況下,您可以自由混合 MyISAM
表格的並行 INSERT
和 SELECT
語句,而無需鎖定。也就是說,您可以在其他客戶端從 MyISAM
表格讀取資料的同時,將資料列插入其中。MyISAM
表格的中間如果有資料列被刪除或更新,就會產生空洞。如果有空洞,則會停用並行插入,但當所有空洞都填滿新資料時,會自動再次啟用。若要控制此行為,請使用 concurrent_insert
系統變數。請參閱第 10.11.3 節「並行插入」。
如果您使用 LOCK TABLES
明確取得表格鎖定,您可以請求 READ LOCAL
鎖定,而不是 READ
鎖定,以在您鎖定表格時允許其他會話執行並行插入。
若要對表格 t1
執行多個 INSERT
和 SELECT
作業,而當並行插入不可行時,您可以將資料列插入臨時表格 temp_t1
,並使用臨時表格中的資料列更新真實表格。
mysql> LOCK TABLES t1 WRITE, temp_t1 WRITE;
mysql> INSERT INTO t1 SELECT * FROM temp_t1;
mysql> DELETE FROM temp_t1;
mysql> UNLOCK TABLES;
一般來說,在下列情況下,表格鎖定優於資料列層級鎖定:
使用較高層級的鎖定,您可以更輕鬆地透過支援不同類型的鎖定來調整應用程式,因為鎖定額外負荷低於資料列層級鎖定。
資料列層級鎖定以外的選項
版本控制(例如 MySQL 中用於並行插入的版本控制),其中可以同時有一個寫入器和多個讀取器。這表示資料庫或表格根據存取開始的時間支援不同的資料檢視。此類型的其他常見術語為「「時間旅行」」、「「寫入時複製」」或「「依需求複製」」。
在許多情況下,依需求複製優於資料列層級鎖定。然而,在最糟的情況下,它會使用比使用正常鎖定更多的記憶體。
您可以使用應用程式層級的鎖定,而不是使用資料列層級鎖定,例如 MySQL 中
GET_LOCK()
和RELEASE_LOCK()
所提供的鎖定。這些是諮詢鎖定,因此它們僅適用於彼此合作的應用程式。請參閱第 14.14 節「鎖定函數」。