MySQL Reference Manual for version 4.1.0-alpha.


6.7 MySQL 事務與鎖定命令

6.7.1 BEGIN/COMMIT/ROLLBACK 句法

預設的,MySQL 運行在 autocommit 樣式。這就意味著,當你執行完一個更新時,MySQL 將立刻將更新儲存到硬碟上。

如果你使用事務安全表 (例如 InnoDBBDB),通過下面的命令,你可以設定 MySQL 為非 autocommit 樣式:

SET AUTOCOMMIT=0

在此之後,你必須使用 COMMIT 來儲存你的變更到硬碟上,或者使用 ROLLBACK ,如果你希望忽略從你的事務開始所做的變更。

如果你希望為一系列語句從 AUTOCOMMIT 樣式轉換,你可以使用 START TRANSACTIONBEGINBEGIN WORK 語句:

START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summmary=@A WHERE type=1;
COMMIT;

START TRANSACTION 在 MySQL 4.0.11 中被加入﹔這是被推荐的開始一個特別(ad-hoc)事務的方式,因為這是 ANSI SQL 句法。

注意,如果你使用的是一個非事務安全表,變更會立刻被儲存,不受 autocommit 樣式狀態的約束。

當你更新了一個非事務表後,如果你執行一個 ROLLBACK,你將得到一個錯誤 (ER_WARNING_NOT_COMPLETE_ROLLBACK) 作為一個警告。所有事務安全表將被恢復,但是非事務安全表將不會改變。

如果你使用 START TRANSACTIONSET AUTOCOMMIT=0,你應該使用 MySQL 二進位日誌做備份以代替老的更新日誌。事務處理被以一個大塊形式儲存在二進位日誌中,在 COMMIT 上面,為了保護回滾的事務,而不是被儲存的。查看章節 4.9.4 二進位日誌。 如果您使用起動事務處理或集AUTOCOMMIT=0 ,您應該使用MySQL 二進位日誌為備份代替更舊的更新日誌。 事務處理儲存在二進位登錄一大塊,做,保証, 滾的事務處理不儲存。 參見部分4 。9.4 二進位日誌。

下列命令自動的結束一個事務 (就好像你在執行這個命令之前,做了一個 COMMIT):

命令 命令 命令
ALTER TABLE BEGIN CREATE INDEX
DROP DATABASE DROP TABLE RENAME TABLE
TRUNCATE    

你可以使用 SET TRANSACTION ISOLATION LEVEL ... 改變事務的隔離級。查看章節 6.7.3 SET TRANSACTION 句法

6.7.2 LOCK TABLES/UNLOCK TABLES 句法

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,或當與伺服器的連線被關閉時,被目前線程鎖定的所有表將被自動地解鎖。

為了在 MySQL 4.0.2 使用 LOCK TABLES ,你必須擁有一個全局的 LOCK TABLES 權限和一個在相關表上的 SELECT 權限。在 MySQL 3.23 中,你對該表需要有 SELECTinsertDELETEUPDATE 權限。

使用 LOCK TABLES 的主要原因是,仿效事務處理或在更新表時得到更快的速度。此後會有更詳細的描述。

如果一個線程在一個表上得到一個 READ 鎖,該線程 (和所有其它線程) 只能從表中讀取。如果一個線程在一個表上得到一個 WRITE 鎖,那麼只有擁有這個鎖的線程可以從表中讀取和寫表。其它的線程被阻塞。

READ LOCALREAD 之間的不同就在於,當鎖被加載時,READ LOCAL 允許非衝突(non-conflicting) INSERT 語句執行。如果當你加載著鎖時從 MySQL 外部操作資料庫文件,這將仍不能被使用。

當你使用 LOCK TABLES 是地,你必須鎖定所有你將使用的表,並且必須使用與你的查詢中將使用的別名相同!如果你在一個查詢中多次使用一個表(用別名),你必須為每一個別名獲得一個鎖。

WRITE 鎖通過比 READ 鎖有更高的權限,以確保更新被盡快地處理。這就意味著,如果一個線程獲得一個 READ 鎖,而同時另外一個線程請求一個 WRITE 鎖,並發的 READ 鎖請求將等待直到 WRITE 線程得到了鎖並釋放了它。你可以使用 LOW_PRIORITY WRITE 鎖,當該線程在等待 WRITE 鎖時,它將允許其它的線程獲得 READ 鎖。 你應該只使用 LOW_PRIORITY WRITE 鎖,如果你確信這將是最後一次,當沒有線程將擁有 READ 鎖。

LOCK TABLES 工作如下:

  1. 以內部定義的次序排序所有被鎖定的表 (從使用者立場說,該次序是不明確的)。
  2. 如果一個表被以一個讀鎖和一個寫鎖鎖定,將寫鎖放在讀鎖之前。
  3. 一次只鎖定一個表,只到線程得到所有的鎖定。

這個方案是為了確保,表鎖定死鎖釋放。 對於這個樣式你仍然有些其它事情需要知道:

如果你對一個表使用一個 LOW_PRIORITY WRITE 鎖定,這就意味著,MySQL 將等待這個鎖,直到沒有線程請求一個 READ 鎖。當線程得到了 WRITE 鎖,並等待獲得鎖定表清單中的下一個表的鎖定時,其它所有的線程將等待 WRITE 鎖被釋放。如果這在你的應用程式中會引起一個嚴重的問題,你應該考慮將你的某些表轉換為事務安全表。

你可以使用 KILL 安全地殺死一個正在表鎖定的線程。查看章節 4.5.5 KILL 句法

注意,你不應該 鎖定你正在對其使用 INSERT DELAYED 的表。這是因為,在這種情況下,INSERT 是通過單獨的線程完成的。

通常,你不需要鎖定任何表,因為所有單 UPDATE 語句都是原幾的﹔其它的線程無法幹擾目前執行的 SQL 語句。當你無論如何希望鎖定表時,這裡有一些情況:

通過使用遞增更新 (UPDATE customer SET value=value+new_value) 或 LAST_INSERT_ID() 函數,你可以在很多情況下避免使用 LOCK TABLES

你也可以使用使用者級鎖定函數 GET_LOCK()RELEASE_LOCK() 解決一些情況,這些鎖被保存在伺服器上的一個哈希表中,並以 pthread_mutex_lock()pthread_mutex_unlock() 實現以獲得高速度。查看章節 6.3.6.2 輔助功能函數

查看章節 5.3.1 MySQL 如何鎖定表,以獲取關於鎖定方案的更多資訊。

你可以使用 FLUSH TABLES WITH READ LOCK 命令以讀鎖鎖定所有資料庫中的所有表。查看章節 4.5.3 FLUSH 句法。如果你有一個可以及時建立文件快照的文件系統,例如 Veritas,這將是得到備份的非常方便方式。

注意:LOCK TABLES 不是事務安全的,在嘗試鎖定一個表之前,將自動地提交所有的活動事務。

6.7.3 SET TRANSACTION 句法

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }

設定全局的、整個會話或下一個事務的事務隔離級。

預設行為是設定下一個(未啟動的)事務的隔離級。如果你使用 GLOBAL 關鍵詞,語句為所有在那個點上建立的新連線設定預設的全局事務隔離級。為了這樣做,你需要有 SUPER 權限。使用 SESSION 關鍵詞為目前連線所有將來執行的事務設定預設的事務隔離級。

你可以使用 --transaction-isolation=...mysqld 設定預設的全局隔離級。查看章節 4.1.1 mysqld 命令行選項