MySQL鎖之表級鎖

在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;
Select sum(subtotal) from order_detail;

這時,如果不先給兩個表加鎖,就可能產生錯誤的結果,因爲第一條語句執行過程中,order_detail表可能已經發生了改變。因此,正確的方法應該是:

Lock tables orders read local, order_detail read local;
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
Unlock tables;

要特別說明以下兩點內容。

·上面的例子在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_textREAD鎖定

當前session可以查詢該表記錄                                                                                                  其他session也可以查詢該表的記錄

    當前session不能查詢沒有鎖定的表                                                                                          其他session可以查詢或者更新未鎖定的表

    當前session中插入或者更新鎖定的表都會提示錯誤:                                                         其他session更新鎖定表會等待獲得鎖:

    釋放鎖                                                                                                                                         Session獲得鎖,更新操作完成:

當使用LOCK TABLES時,不僅需要一次鎖定用到的所有表,而且,同一個表在SQL語句中出現多少次,就要通過與SQL語句中相同的別名鎖定多少次,否則也會出錯

在一定條件下,MyISAM表也支持查詢和插入操作的併發進行。


參看自:深入淺出MySQL——數據庫開發、優化與管理維護

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章