Myisam引擎鎖

Mysql的鎖機制也分爲三大類:

 

page Lock頁級鎖 、 table LOCK 表鎖  、行鎖

 

page Lock頁級鎖:NDB事務引擎。

 

首先查看當前的鎖狀態--

 

mysql>show status like ‘table%’;

+--------------------------------- +-------+

| Variable_name                   | Value  |

+---------------------------------+--------+

| Table_locks_immediate  | 2979   |

| Table_locks_waited              | 0      |

+---------------------------------+--------+

如果table_locks_waited的值過高的話,就會導致表鎖爭搶嚴重,應該做出調整。

 

Myisam引擎的表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)

 

對MyISAM表的讀操作,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;

對MyISAM表的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;MyISAM表的讀操作與寫操作之間,以及寫操作之間是串行的.

 

Demo for Lock:(表獨佔寫鎖(Table Write Lock))

lock table table_name write;

此時對table_name這張表加了表獨佔寫鎖(Table Write Lock),

導致其他session無法訪問table_name中的數據信息,就是read操作也無法執行。

當一個線程獲得對一個表的寫鎖後,只有持有鎖的線程可以對錶進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放爲止。

mysql> lock table world.city write;

Query OK, 0 rows affected (0.00 sec)

 

session2:此時的狀態

mysql>use world

Database changed

mysql>select count(*) from city;

處於等待。

 

直到session1執行,unlock tables;

session2:

mysql>select count(*) from city;

+----------+

| count(*) |

+----------+

|     4081 |

+----------+

1 row in set (51.65 sec)

 

 

Myisam 表在做select查詢的時候都會隱式的加一個讀鎖給相關的表,只能讀不能寫。。

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,

在執行更新操作(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,

這個過程並不需要用戶干預,因此,用戶一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。

給MyISAM表顯示加鎖,一般是爲了在一定程度模擬事務操作,實現對某一時間點多個表的一致性讀取

 

 

Lock tables orders read local, order_detail read local;

Select count(clause) from orders;

Select sum(clause) from order_detail;

Unlock tables;

上面的例子在LOCK TABLES時加了“local”選項,其作用就是在滿足MyISAM表併發插入條件的情況下,

允許其他用戶在表尾併發插入記錄,有關MyISAM表的併發插入問題,在後面的章節中還會進一步介紹。

 

mysql> lock table city read;

Query OK, 0 rows affected (0.01 sec)

 

mysql> select * from country;

ERROR 1100 (HY000): Table 'country' was not locked with LOCK TABLES

myisam表在加鎖以後只能訪問這些已經顯示加了鎖的表。

mysql> insert into city (ID) values(98876555)

    -> ;

ERROR 1099 (HY000): Table 'city' was locked with a READ lock and can't be updated

myisam表加了讀鎖,就只能執行讀操作,不能更新表數據,

 

 

 

 

一個session使用LOCK TABLE命令給表film_text加了讀鎖,這個session可以查詢鎖定表中的記錄,但更新或訪問其他表都會提示錯誤;同時,另外一個session可以查詢表中的記錄,但更新就會出現鎖等待。

表20-3                     MyISAM存儲引擎的讀阻塞寫例子

 

session_1

session_2

獲得表film_text的READ鎖定

mysql> lock table film_text read;

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    | ACADEMY DINOSAUR |

+---------+------------------+

1 row in set (0.00 sec)

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

mysql> select film_id,title from film_text where film_id = 1001;

+---------+------------------+

| film_id | title            |

+---------+------------------+

| 1001    | ACADEMY DINOSAUR |

+---------+------------------+

1 row in set (0.00 sec)

當前session不能查詢沒有鎖定的表

mysql> select film_id,title from film where film_id = 1001;

ERROR 1100 (HY000): Table 'film' was not locked with LOCK TABLES

其他session可以查詢或者更新未鎖定的表

mysql> select film_id,title from film where film_id = 1001;

+---------+---------------+

| film_id | title         |

+---------+---------------+

| 1001    | update record |

+---------+---------------+

1 row in set (0.00 sec)

mysql> update film set title = 'Test' where film_id = 1001;

Query OK, 1 row affected (0.04 sec)

Rows matched: 1  Changed: 1  Warnings: 0

當前session中插入或者更新鎖定的表都會提示錯誤:

mysql> insert into film_text (film_id,title) values(1002,'Test');

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

mysql> update film_text set title = 'Test' where film_id = 1001;

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

其他session更新鎖定表會等待獲得鎖:

mysql> update film_text set title = 'Test' where film_id = 1001;

等待

釋放鎖

mysql> unlock tables;

Query OK, 0 rows affected (0.00 sec)

等待

 

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

mysql> update film_text set title = 'Test' where film_id = 1001;

Query OK, 1 row affected (1 min 0.71 sec)

Rows matched: 1  Changed: 1  Warnings: 0

 
mysql> lock table actor read;
Query OK, 0 rows affected (0.00 sec)
但是通過別名訪問會提示錯誤:
mysql> select a.first_name,a.last_name,b.first_name,b.last_name from actor a,actor b where a.first_name = b.first_name and a.first_name = 'Lisa' and a.last_name = 'Tom' and a.last_name <> b.last_name;
ERROR 1100 (HY000): Table 'a' was not locked with LOCK TABLES
需要對別名分別鎖定:
mysql> lock table actor as a read,actor as b read;
Query OK, 0 rows affected (0.00 sec)
 
 
3:     上文提到過MyISAM表的讀和寫是串行的,但這是就總體而言的。在一定條件下,MyISAM表也支持查詢和插入操作的併發進行。

MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別可以爲0、1或2。

     當concurrent_insert設置爲0時,不允許併發插入。

     當concurrent_insert設置爲1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),

MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設置。

     當concurrent_insert設置爲2時,無論MyISAM表中有沒有空洞,都允許在表尾併發插入記錄。

 

session_1獲得了一個表的READ LOCAL鎖,該線程可以對錶進行查詢操作,但不能對錶進行更新操作;其他的線程(session_2),雖然不能對錶進行刪除和更新操作,但卻可以對該表進行併發插入操作,這裏假設該表中間不存在空洞。

表20-4              MyISAM存儲引擎的讀寫(INSERT)併發例子

 

session_1

session_2

獲得表film_text的READ LOCAL鎖定

mysql> lock table film_text read local;

Query OK, 0 rows affected (0.00 sec)

 

當前session不能對鎖定表進行更新或者插入操作:

mysql> insert into film_text (film_id,title) values(1002,'Test');

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

mysql> update film_text set title = 'Test' where film_id = 1001;

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

其他session可以進行插入操作,但是更新會等待:

mysql> insert into film_text (film_id,title) values(1002,'Test');

Query OK, 1 row affected (0.00 sec)

mysql> update film_text set title = 'Update Test' where film_id = 1001;

等待

當前session不能訪問其他session插入的記錄:

mysql> select film_id,title from film_text where film_id = 1002;

Empty set (0.00 sec)

 

釋放鎖:

mysql> unlock tables;

Query OK, 0 rows affected (0.00 sec)

等待

當前session解鎖後可以獲得其他session插入的記錄:

mysql> select film_id,title from film_text where film_id = 1002;

+---------+-------+

| film_id | title |

+---------+-------+

| 1002    | Test  |

+---------+-------+

1 row in set (0.00 sec)

Session2獲得鎖,更新操作完成:

mysql> update film_text set title = 'Update Test' where film_id = 1001;

Query OK, 1 row affected (1 min 17.75 sec)

Rows matched: 1  Changed: 1  Warnings: 0

        可以利用MyISAM存儲引擎的併發插入特性,來解決應用中對同一表查詢和插入的鎖爭用。例如,將concurrent_insert系統變量設爲2,總是允許併發插入;同時,通過定期在系統空閒時段執行OPTIMIZE TABLE語句來整理空間碎片,收回因刪除記錄而產生的中間空洞。

 

Myisam鎖調度:

 

MyISAM存儲引擎的讀鎖和寫鎖是互斥的,讀寫操作是串行的。那麼,一個進程請求某個MyISAM表的讀鎖,同時另一個進程也請求同一表的寫鎖,MySQL如何處理呢?答案是寫進程先獲得鎖。不僅如此,即使讀請求先到鎖等待隊列,寫請求後到,寫鎖也會插到讀鎖請求之前!這是因爲MySQL認爲寫請求一般比讀請求要重要。這也正是MyISAM表不太適合於有大量更新操作和查詢操作應用的原因,因爲,大量的更新操作會造成查詢操作很難獲得讀鎖,從而可能永遠阻塞。這種情況有時可能會變得非常糟糕!幸好我們可以通過一些設置來調節MyISAM的調度行爲。

        通過指定啓動參數low-priority-updates,使MyISAM引擎默認給予讀請求以優先的權利。

       通過執行命令SET LOW_PRIORITY_UPDATES=1,使該連接發出的更新請求優先級降低。

       通過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先級。

        雖然上面3種方法都是要麼更新優先,要麼查詢優先的方法,但還是可以用其來解決查詢相對重要的應用(如用戶登錄系統)中,讀鎖等待嚴重的問題。

        另外,MySQL也提供了一種折中的辦法來調節讀寫衝突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先級降低,給讀進程一定獲得鎖的機會。

        上面已經討論了寫優先調度機制帶來的問題和解決辦法。這裏還要強調一點:一些需要長時間運行的查詢操作,也會使寫進程“餓死”!因此,應用中應儘量避免出現長時間運行的查詢操作,不要總想用一條SELECT語句來解決問題,因爲這種看似巧妙的SQL語句,往往比較複雜,執行時間較長,在可能的情況下可以通過使用中間表等措施對SQL語句做一定的“分解”,使每一步查詢都能在較短時間完成,從而減少鎖衝突。如果複雜查詢不可避免,應儘量安排在數據庫空閒時段執行,比如一些定期統計可以安排在夜間執行。

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