在MySQL中支持表級鎖的主要有MyISAM存儲引擎和InnoDB存儲引擎,其中MyISAM存儲引擎只支持表級鎖,而InnoDB既支持表級鎖也支持行級索。一般使用表級鎖時使用MyISAM較多。
MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。
共享讀鎖也叫共享鎖,某一事物讀取數據時加鎖,其他事物在讀取數據時亦可加共享鎖,但是不能存在有事物加排它鎖的情況。如果有加排它鎖的其他事物需要執行,那麼必須等待釋放共享鎖。
獨佔寫鎖也叫排它鎖,某一事物在寫數據時需要加上排它鎖,確保其他事物不能修改數據。如果某一事物加上了排它鎖,那麼同一時間不能同時存在另外的排它鎖和共享鎖。如果有加排他鎖或共享鎖的其他事物需要執行,那麼必須等待排它鎖的釋放。
請求鎖模式
是否兼容 當前鎖模式 |
None |
讀鎖 |
寫鎖 |
讀鎖 |
是 |
是 |
否 |
寫鎖 |
是 |
否 |
否 |
可見,對MyISAM表的讀操作,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;
對MyISAM表的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;MyISAM表的讀操作與寫操作之間,以及寫操作之間是串行的!
根據下圖所示的例子可以知道,當一個線程獲得對一個表的寫鎖後,只有持有鎖的線程可以對錶進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放爲止。
MyISAM存儲引擎的寫阻塞讀例子
session_1 |
session_2 |
獲得表film_text的WRITE鎖定 mysql> lock table film_text write; Query OK, 0 rows affected (0.00 sec) |
|
當前session對鎖定表的查詢、更新、插入操作都可以執行: mysql> select film_id,title from film_text where film_id = 1001; +---------+-------------+ | film_id | title | +---------+-------------+ | 1001 | Update Test | +---------+-------------+ 1 row in set (0.00 sec)
mysql> insert into film_text (film_id,title) values(1003,'Test'); Query OK, 1 row affected (0.00 sec)
mysql> update film_text set title = 'Test' where film_id = 1001; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
其他session對鎖定表的查詢被阻塞,需要等待鎖被釋放: mysql> select film_id,title from film_text where film_id = 1001; 等待 |
釋放鎖: mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) |
等待 |
|
Session2獲得鎖,查詢返回: mysql> select film_id,title from film_text where film_id = 1001; +---------+-------+ | film_id | title | +---------+-------+ | 1001 | Test | +---------+-------+ 1 row in set (57.59 sec) |
上圖是顯示加鎖,但在MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不需要用戶干預,因此,用戶一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。
給MyISAM表顯示加鎖,一般是爲了在一定程度模擬事務操作,實現對某一時間點多個表的一致性讀取。例如,有一個訂單表orders,其中記錄有各訂單的總金額total,同時還有一個訂單明細表order_detail,其中記錄有各訂單每一產品的金額小計subtotal,假設我們需要檢查這兩個表的金額合計是否相符,可能就需要執行如下兩條SQL:
Select sum(total) from orders; |
這時,如果不先給兩個表加鎖,就可能產生錯誤的結果,因爲第一條語句執行過程中,order_detail表可能已經發生了改變。因此,正確的方法應該是:
Lock tables orders read local, order_detail read local; |
要特別說明以下兩點內容。
·上面的例子在LOCK TABLES時加了“local”選項,其作用就是在滿足MyISAM表併發插入條件的情況下,允許其他用戶在表尾併發插入記錄,有關MyISAM表的併發插入問題,在後面的章節中還會進一步介紹。
· 在用LOCK TABLES給表顯式加表鎖時,必須同時取得所有涉及到表的鎖,並且MySQL不支持鎖升級。也就是說,在執行LOCK TABLES後,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表;同時,如果加的是讀鎖,那麼只能執行查詢操作,而不能執行更新操作。其實,在自動加鎖的情況下也基本如此,MyISAM總是一次獲得SQL語句所需要的全部鎖。這也正是MyISAM表不會出現死鎖(Deadlock Free)的原因。
一個session使用LOCK TABLE命令給表film_text加了讀鎖,這個session可以查詢鎖定表中的記錄,但更新或訪問其他表都會提示錯誤;同時,另外一個session可以查詢表中的記錄,但更新就會出現鎖等待。
session1 session2
獲得表film_text的READ鎖定
當前session可以查詢該表記錄 其他session也可以查詢該表的記錄
當前session不能查詢沒有鎖定的表 其他session可以查詢或者更新未鎖定的表
當前session中插入或者更新鎖定的表都會提示錯誤: 其他session更新鎖定表會等待獲得鎖:
釋放鎖 Session獲得鎖,更新操作完成:
當使用LOCK TABLES時,不僅需要一次鎖定用到的所有表,而且,同一個表在SQL語句中出現多少次,就要通過與SQL語句中相同的別名鎖定多少次,否則也會出錯
在一定條件下,MyISAM表也支持查詢和插入操作的併發進行。
參看自:深入淺出MySQL——數據庫開發、優化與管理維護