mysql查詢更新時的鎖表機制分析

爲了給高併發狀況下的mysql進行更好的優化,有必要了解一下mysql查詢更新時的鎖表機制。 css

1、概述 mysql

MySQL有三種鎖的級別:頁級、表級、行級。
MyISAM和MEMORY存儲引擎採用的是表級鎖(table-level locking);BDB存儲引擎採用的是頁面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認狀況下是採用行級鎖。 sql

MySQL這3種鎖的特性可大體概括以下: 併發

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。 負載均衡

2、MyISAM表鎖 高併發

MyISAM存儲引擎只支持表鎖,是如今用得最多的存儲引擎。 大數據

一、查詢表級鎖爭用狀況 優化

能夠經過檢查table_locks_waited和table_locks_immediate狀態變量來分析系統上的表鎖定爭奪: spa

mysql> show status like ‘table%’;
+-----------------------+----------+
| Variable_name | Value |
+-----------------------+----------+
| Table_locks_immediate | 76939364 |
| Table_locks_waited | 305089 |
+-----------------------+----------+
2 rows in set (0.00 sec) 索引

Table_locks_waited的值比較高,說明存在着較嚴重的表級鎖爭用狀況。

二、MySQL表級鎖的鎖模式

MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖。

因此對MyISAM表進行操做,會有如下狀況:
a、對MyISAM表的讀操做(加讀鎖),不會阻塞其餘進程對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後,纔會執行其它進程的寫操做。
b、對MyISAM表的寫操做(加寫鎖),會阻塞其餘進程對同一表的讀和寫操做,只有當寫鎖釋放後,纔會執行其它進程的讀寫操做。

下面經過例子來進行驗證以上觀點。數據表gz_phone裏有二百多萬數據,字段id,phone,ua,day。如今同時用多個客戶端同時對該表進行操做分析。
a、當我用客戶端1進行一個比較長時間的讀操做時,分別用客戶端2進行讀和寫操做:
client1:

mysql>select count(*) from gz_phone group by ua;
75508 rows in set (3 min 15.87 sec)

client2:

select id,phone from gz_phone limit 1000,10;
+------+-------+
| id | phone |
+------+-------+
| 1001 | 2222 |
| 1002 | 2222 |
| 1003 | 2222 |
| 1004 | 2222 |
| 1005 | 2222 |
| 1006 | 2222 |
| 1007 | 2222 |
| 1008 | 2222 |
| 1009 | 2222 |
| 1010 | 2222 |
+------+-------+
10 rows in set (0.01 sec)

mysql> update gz_phone set phone=’11111111111′ where id=1001;
Query OK, 0 rows affected (2 min 57.88 sec)
Rows matched: 1 Changed: 0 Warnings: 0

說明當數據表有一個讀鎖時,其它進程的查詢操做能夠立刻執行,但更新操做需等待讀鎖釋放後纔會執行。

b、當用客戶端1進行一個較長時間的更新操做時,用客戶端2,3分別進行讀寫操做:
client1:

mysql> update gz_phone set phone=’11111111111′;
Query OK, 1671823 rows affected (3 min 4.03 sec)
Rows matched: 2212070 Changed: 1671823 Warnings: 0

client2:

mysql> select id,phone,ua,day from gz_phone limit 10;
+----+-------+-------------------+------------+
| id | phone | ua | day |
+----+-------+-------------------+------------+
| 1 | 2222 | SonyEricssonK310c | 2007-12-19 |
| 2 | 2222 | SonyEricssonK750c | 2007-12-19 |
| 3 | 2222 | MAUI WAP Browser | 2007-12-19 |
| 4 | 2222 | Nokia3108 | 2007-12-19 |
| 5 | 2222 | LENOVO-I750 | 2007-12-19 |
| 6 | 2222 | BIRD_D636 | 2007-12-19 |
| 7 | 2222 | SonyEricssonS500c | 2007-12-19 |
| 8 | 2222 | SAMSUNG-SGH-E258 | 2007-12-19 |
| 9 | 2222 | NokiaN73-1 | 2007-12-19 |
| 10 | 2222 | Nokia2610 | 2007-12-19 |
+----+-------+-------------------+------------+
10 rows in set (2 min 58.56 sec)

client3:

mysql> update gz_phone set phone=’55555′ where id=1;
Query OK, 1 row affected (3 min 50.16 sec)
Rows matched: 1 Changed: 1 Warnings: 0

說明當數據表有一個寫鎖時,其它進程的讀寫操做都需等待讀鎖釋放後纔會執行。

三、併發插入

原則上數據表有一個讀鎖時,其它進程沒法對此表進行更新操做,但在必定條件下,MyISAM表也支持查詢和插入操做的併發進行。

MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別能夠爲0、1或2。
a、當concurrent_insert設置爲0時,不容許併發插入。
b、當concurrent_insert設置爲1時,若是MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM容許在一個進程讀表的同時,另外一個進程從表尾插入記錄。這也是MySQL的默認設置。
c、當concurrent_insert設置爲2時,不管MyISAM表中有沒有空洞,都容許在表尾併發插入記錄。

四、MyISAM的鎖調度

因爲MySQL認爲寫請求通常比讀請求要重要,因此若是有讀寫請求同時進行的話,MYSQL將會優先執行寫操做。這樣MyISAM表在進行大量的更新操做時(特別是更新的字段中存在索引的狀況下),會形成查詢操做很難得到讀鎖,從而致使查詢阻塞。

咱們能夠經過一些設置來調節MyISAM的調度行爲:

a、經過指定啓動參數low-priority-updates,使MyISAM引擎默認給予讀請求以優先的權利。
b、經過執行命令SET LOW_PRIORITY_UPDATES=1,使該鏈接發出的更新請求優先級下降。
c、經過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,下降該語句的優先級。

上面3種方法都是要麼更新優先,要麼查詢優先的方法。這裏要說明的就是,不要盲目的給mysql設置爲讀優先,由於一些須要長時間運行的查詢操做,也會使寫進程「餓死」。只有根據你的實際狀況,來決定設置哪一種操做優先。這些方法仍是沒有從根本上同時解決查詢和更新的問題。

在一個有大數據量高並發表的mysql裏,咱們還可採用另外一種策略來進行優化,那就是經過mysql主從(讀寫)分離來實現負載均衡,這樣可避免優先哪種操做從而可能致使另外一種操做的堵塞。下面將用一個篇幅來講明mysql的讀寫分離技術。

相關文章
相關標籤/搜索