一、鎖的概念
- 鎖是計算機協調多個進程或線程併發訪問某一資源的機制。
- 在數據庫中,數據也是一種供許多用戶共享的資源。如何保證數據併發訪問的一致性、有效性是所有數據庫必須解決的一個問題,鎖衝突也是影響數據庫併發訪問性能的一個重要因素。
- 鎖對數據庫而言顯得尤其重要,也更加複雜。
二、MyISAM引擎的鎖
mysam引擎只有表級鎖:
1.共享讀鎖
序號 | session1 | session2 |
---|---|---|
1 | lock table testmysam READ | |
2 | select * from testmysam (可以查詢) | select * from testmysam (可以查詢) |
3 | insert into testmysam value(2); (報錯) | |
4 |
update testmysam set id=2 where id=1;(報錯) |
|
5 | insert into testmysam value(2); (阻塞) | |
6 | insert into account value(4,'aa',123); (報錯) | |
7 | select * from account ; (報錯) | |
8 |
insert into account value(4,'aa',123); (成功) |
|
9 | select s.* from testmysam s (報錯) |
總結:在session1對錶testmysam加讀鎖之後,session1,只能對testmysam表進行讀操作,不能進行寫操作,也不能對其他表進行任何操作;其他session可以對錶testmysam進行讀操作,寫操作會被阻塞,但是其他session可以對其他表操作。
2.獨佔寫鎖
序號 | session1 | session2 |
---|---|---|
1 | lock table testmysam WRITE | |
2 | insert testmysam value(3);(成功) | |
3 |
delete from testmysam where id = 3 (成功) |
|
4 | select * from testmysam (成功) | |
5 |
select s.* from testmysam s (報錯) |
|
6 |
insert into account value(4,'aa',123); (報錯) |
|
7 |
select * from testmysam (阻塞) |
|
8 | insert testmysam value(3);(阻塞) | |
insert into account value(4,'aa',123); (成功) |
總結:在session1對錶testmysam加寫鎖之後,session1,只能對testmysam表進行任何操作,但不能對其他表進行任何操作;其他session對錶testmysam的任何操作都會被阻塞,但是其他session可以對其他表操作。
二、InnoDB引擎的鎖
mysql的InnoDB引擎支持行鎖,行鎖分爲共享鎖(讀鎖)和 排他鎖(寫鎖)
共享鎖又稱:讀鎖。
當一個事務對某幾行上讀鎖時,允許其他事務對這幾行進行讀操作,但不允許其進行寫操作,也不允許其他事務給這幾行上排它鎖,但允許上讀鎖。讀鎖可以共存。
排它鎖又稱:寫鎖。
當一個事務對某幾個上寫鎖時,不允許其他事務寫,但允許讀。更不允許其他事務給這幾行上任何鎖。包括寫鎖。寫鎖不和任何鎖共存。
上共享鎖的寫法:lock in share mode
例如: select * from tableA where 。。。lock in share mode;
上排它鎖的寫法:for update
例如:select * from tableA where 。。。for update;
注意:
1.行鎖必須有索引才能實現,否則會自動鎖全表,那麼就不是行鎖了。
2.insert ,delete , update在事務中都會自動默認加上排它鎖。
3.兩個事務不能鎖同一個索引。
4.提交事務,回滾事務,新開起一個事務都會釋放行鎖。
InnoDB的行鎖演示
CREATE TABLE testdemo (
`id` int(255) NOT NULL ,
`c1` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`c2` int(50) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
INDEX `idx_c2` (`c2`) USING BTREE
)
ENGINE=InnoDB;
序號 | session1 | session2 |
---|---|---|
1 |
BEGIN select * from testdemo where id =1 for update 在id爲1的記錄上加讀鎖 |
|
2 | update testdemo set c1 = '1' where id = 2 (成功) | |
3 | update testdemo set c1 = '1' where id = 1( 等待) | |
結論:行鎖只能鎖住互譯韓記錄,不影響其他記錄。 | ||
4 |
BEGIN update testdemo set c1 = '1' where id = 1 |
|
update testdemo set c1 = '1' where id = 1( 等待) | ||
結論:修改記錄的操作自動加行鎖。 | ||
5 |
BEGIN update testdemo set c1 = '1' where c1 = '1' |
|
6 | update testdemo set c1 = '2' where c1 = '2' ( 等待) | |
結論:行鎖是基於索引實現的,更新條件沒有索引時,會鎖表。 | ||
7 | select * from testdemo where id =1 for update | |
select * from testdemo where id =1 lock in share mode | ||
8 |
UNLOCK TABLES 並不會解鎖 使用commit 或者 begin或者ROLLBACK 纔會解鎖 |
|
結論:.提交事務,回滾事務,新開起一個事務都會釋放行鎖 |
鎖等待問題
你肯定碰到過這問題,有些程序員在debug程序的時候,經常會鎖住一部分數據庫的數據,而這個時候你也要調試這部分功能,卻發現代碼總是運行超時,你是否碰到過這問題了,其實這問題的根源我相信你也知道了。
舉例來說,有兩個會話。
程序員甲,正直調試代碼
BEGIN
SELECT * FROM testdemo WHERE id = 1 FOR UPDATE
你正直完成的功能也要經過那部分的代碼,你得上個讀鎖
BEGIN
SELECT * FROM testdemo WHERE id = 1 lock in share mode
這個時候很不幸,你並不知道發生了什麼問題,在你調試得過程中永遠就是一個超時得異常,而這種問題不管在開發中還是在實際項目運行中都可能會碰到,那麼怎麼排查這個問題呢?
這其實也是有小技巧的。
select * from information_schema.INNODB_LOCKS;
我通過這個sql語句起碼發現在同一張表裏面得同一個數據有了2個鎖其中一個是X(寫鎖),另外一個是S(讀鎖)
解決:
select * from sys.innodb_lock_waits
執行的這個sql語句看下最下面,kill命令,你在工作中完全可以通過kill吧阻塞了的sql語句給幹掉,你就可以繼續運行了。
如果是MySQL5.6
SELECT
r.trx_id waiting_trx_id,
r.trx_mysql_thread_id waiting_thread,
r.trx_query waiting_query,
b.trx_id blocking_trx_id,
b.trx_mysql_thread_id blocking_thread
FROM
information_schema.innodb_lock_waits w
INNER JOIN
information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN
information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
kill 掉阻塞的線程即可。