MySql的鎖詳解

一、鎖的概念

  1.  鎖是計算機協調多個進程或線程併發訪問某一資源的機制。
  2. 在數據庫中,數據也是一種供許多用戶共享的資源。如何保證數據併發訪問的一致性、有效性是所有數據庫必須解決的一個問題,鎖衝突也是影響數據庫併發訪問性能的一個重要因素。
  3. 鎖對數據庫而言顯得尤其重要,也更加複雜。

二、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 掉阻塞的線程即可。

 

 

 

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