一、概述
MySQL有三種鎖的級別:頁級、表級、行級。
MyISAM存儲引擎采用的是表級鎖(table-level locking);
MySQL這3種鎖的特性可大致歸納如下:
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高。
頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現死鎖;鎖定粒度界于表鎖和行鎖之間,并發度一般。
二、MyISAM表鎖
MyISAM存儲引擎只支持表鎖,是現在用得最多的存儲引擎。
1、查詢表級鎖爭用情況
可以通過檢查table_locks_waited和table_locks_immediate狀態變量來分析系統上的表鎖定爭奪:
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/(Table_locks_immediate+Table_locks_waited)
這個比例值越大說明表級鎖爭用的情況越嚴重。
例:比例值=0.01說明100次進程里就有一次是需要等待鎖的進程;
2、MySQL表級鎖的鎖模式
MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫鎖(Table WriteLock)。MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖。所以對MyISAM表進行操作,會有以下情況:
讀鎖:當某一進程對某張表進行讀操作時(select),其他線程也可以讀,但是不能寫。簡單的理解就是,我讀的時候你不能寫。
寫鎖:當某一進程對某種表某張表的寫時(insert,update,,delete),其他線程不能寫也不能讀。可以理解為,我寫的時候,你不能讀,也不能寫。
因此MyISAM的讀操作和寫操作,以及寫操作之間是串行的!MyISAM在執行讀寫操作的時候會自動給表加相應的鎖(也就是說不用顯示的使用lock table命令),MyISAM總是一次獲得SQL語句所需要的全部鎖,這也是MyISAM不會出現死鎖的原因。
3、并發插入
原則上數據表有一個讀鎖時,其它進程無法對此表進行更新操作,但在一定條件下,MyISAM表也支持查詢和插入操作的并發進行。MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其并發插入的行為,其值分別可以為0、1或2。
a、當concurrent_insert設置為0時,不允許并發插入。
b、當concurrent_insert設置為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設置。
c、當concurrent_insert設置為2時,無論MyISAM表中有沒有空洞,都允許在表尾并發插入記錄。
例子:
當前進程:
1)mysql> lock table first_test read local;--加入local選項是說明,在表滿足并發插入的前提下,允許在末尾插入數據。當前進程不能進行插入和更新操作
Query OK, 0 rows affected (0.00 sec)
2)mysql> insert into first_test(age) values(15);
ERROR 1099 (HY000): Table 'first_test' was locked
with a READ lock and can't be updated
3)mysql> update first_test set age=200 where id =1;
ERROR 1099 (HY000): Table 'first_test' was locked
with a READ lock and can't be updated
其他進程:
1)mysql> insert into first_test(age) values(15);--其他進程可以進行插入、查詢和更新操作,但是更新操作要等待。。。
Query OK, 1 row affected (0.00 sec)
2)mysql> update first_test set age=200 where id =2;
等待.....
3)mysql> select * from first_test;
+----+----+
| id |age |
+----+---+
| 1 | 100|
| 2 | 11 |
| 3 | 12 |
| 4 | 13 |
| 5 | 14 |
| 6 | 14 |
+---+-- -+
6 rows in set (0.00 sec)
釋放鎖以后皆大歡喜
mysql> unlock table;
Query OK, 0 rows affected (0.00 sec)
4、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設置為讀優先,因為一些需要長時間運行的查詢操作,也會使寫進程“餓死”。只有根據實際情況,來決定設置哪種操作優先。