innodb事務鎖


計算機程序鎖

 
  • 控制對共享資源進行並發訪問
  • 保護數據的完整性和一致性
 

 

lock  主要是事務,數據庫邏輯內容,事務過程
latch/mutex 內存底層鎖;
 
更新丟失

原因:
B的更改還沒有提交時,A已經再次修改了數據。
此時A使用原來的元數據作為基礎更新后,B的更新便會丟失;
 
解決辦法:
在修改數據上加寫鎖,當有鎖時,A會等B更新提交完,才可以繼續在B的基礎上繼續更新;
 

 

 
事務鎖粒度

 
行鎖: innodb ,oracle
頁鎖:sql server
表鎖:Myisam ,memory
 
獲取innodb行鎖爭用情況
 
mysql> show status like '%innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 0     |
| Innodb_row_lock_time_avg      | 0     |
| Innodb_row_lock_time_max      | 0     |
| Innodb_row_lock_waits         | 0     |
+-------------------------------+-------+
5 rows in set (0.00 sec)
如果發現鎖爭用比較嚴重,如innodb_row_lock_waits 和 innodb_row_lock_time_avg的值比較高,
還可以通過設置innodb monitor 來進一步觀察發生鎖沖突的表,數據行等,並分析鎖爭用的原因:
 
 
innodb鎖模式與粒度

 
四種基本鎖模式
  • 共享鎖(S)-讀鎖-行鎖
  • 排他鎖(X)-寫鎖-行鎖
  • 意向共享鎖(IS)-表級 :事務想要獲得一張表中某幾行的共享鎖
  • 意向排他鎖(IX)-表級:事務想要獲得一張表中某幾行的排他鎖
 
意向鎖,簡單來說就是:
如需要對頁上的記錄R進行X鎖,那么分別需要對該記錄所在的數據庫,表,頁,上意向鎖IX,最后對記錄R上X鎖。
若其中任何一個部分導致等待,那么該操作需要等待粗粒度鎖的完成。
 
innodb支持意向鎖設計比較簡練,其意向鎖即為表級別的鎖。設計目的主要是為了在一個事務中揭示下一行將被請求的鎖類型。
 
意向鎖:
  • 意向鎖總是自動先加,並且意向鎖自動加自動釋放
  • 意向鎖提示數據庫這個session將要在接下來將要施加何種鎖
  • 意向鎖和X/S 鎖級別不同,除了阻塞全表級別的X/S鎖外其他任何鎖 
自動施加,自動釋放,
 
 
innodb鎖模式互斥

 

數據庫加鎖操作
 
一般的select語句不加任何鎖,也不會被任何事物鎖阻塞
讀的隔離性由MVCC確保
 
undo log 用來幫助事務回滾及MVCC(多版本並發控制 ,即select時可以使用行數據的快照,而不用等待鎖資源)
 
S鎖
  手動:select * from tb_test lock in share mode;
  自動:insert前
 
X鎖
   手動:
select *  from tb_test   for update;

   自動:update,delete 前

 
線上環境中:
 
鎖等待時間:innodb_lock_wait_timeout
 
mysql>show global variables like "%wait%"
 
innodb 行鎖

 
通過索引項加鎖實現
  • 只有條件走索引才能實現行級鎖                    a)
  • 索引上有重復值,可能鎖住多個記錄              b)
  • 查詢有多個索引可以走,可以對不同索引加鎖   c)
  • 是否對索引加鎖實際上取決於Mysql執行計划
 
自增主鍵做條件更新,性能做好;
 
通過索引項加鎖實現的例子:
a) 只有,有條件走索引才能實現行級鎖
 
mysql> show create table t2\G;
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
 
mysql> select * from t2;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    1 |    3 |
+------+------+
 
此時A連接 在b =2 時加 寫鎖;
mysql> select * from t2 where b =2 for update;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+
而此時再B連接中再對b=3,加寫鎖時,失敗;
mysql> select * from t2 where b=3 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
說明,表中沒有索引時,innodb將對整個表加鎖,而不能體現行鎖的特性;
 
 
 b)  索引上有重復值,可能鎖住多個記錄 
 
mysql> show create table t2\G;
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> select * from t2;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    1 |    3 |
|    2 |    9 |
+------+------+
 
在A連接中,在a=1,b=2處加一個寫鎖;實際上 是在a=1這個索引上加的鎖
mysql> select * from t2 where a=1 and b=2 for update;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+
1 row in set (0.00 sec)
 
在B連接中,在a=1 and b=3處加寫鎖失敗,因都是a=1這個索引,而A中已經對a=1這個索引的行加過了鎖;
mysql> select * from t2 where a =1 and b=3 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
 
此時B連接是可以對 a=2 and b =9 這一行中,在a=2 這個索引上加鎖的;
mysql> select * from t2 where a=2 and b =9 for update ;
+------+------+
| a    | b    |
+------+------+
|    2 |    9 |
+------+------+
注意
行鎖升級成表鎖
mysql> select * from t2 where  b =9 for update ;
這句對本意在b=9這行加索引,b又沒有加索引,所以這是對整個表加鎖;因為沒有指定a =2,所以mysql找不到a這個索引的;
 
c)  查詢有多個索引可以走,可以對不同索引加鎖
 
mysql> show create table t2\G;
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  KEY `a` (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
mysql> select * from t2;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    1 |    3 |
|    2 |    9 |
+------+------+
在A連接中對 a=1 and b=2 加鎖;
mysql> select * from t2 where a =1 and b =2  for update;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+
 
此時B連接中對a =1 and b=3 ,也是可以加鎖的;這是因為mysql 可以從a=1這個索引來加鎖,也可以對b=3加鎖;
所以就與上面b)中只能對a=1索引來加鎖 區別開來;
 
mysql> select * from t2 where a =1 and b =3  for update;
+------+------+
| a    | b    |
+------+------+
|    1 |    3 |
+------+------+

 

innodb的gap lock 間隙鎖
 
gap lock消滅幻讀
     innodb消滅幻讀僅僅為了確保 statement模式 replicate的主從一致性
 
小心gap lock
 
自增主鍵做條件更新,性能最好;
 
gap lock 間隙鎖 解釋:
 
mysql> select * from t2;
+------+------+
| a    | b    |
+------+------+
|   20 |    2 |
|   24 |    4 |
|   27 |    5 |
|   27 |    6 |
|   27 |    8 |
|   30 |    6 |
|   31 |    4 |
|   32 |    9 |
+------+------+
8 rows in set (0.00 sec)
 
在A連接中給a=27 加鎖(a 是有索引的)
mysql> select * from t2 where a=27 for update;
+------+------+
| a    | b    |
+------+------+
|   27 |    5 |
|   27 |    6 |
|   27 |    8 |
+------+------+
3 rows in set (0.00 sec)
 
此時隔離等級是Repeatable  Read,標准的是可以出現幻讀現象的,
即在B連接中 insert into t2 values(27,3),是可以插入成功的,而且B連接提交后,A連接是可以查看到增加的,27,3這一行的。
 
而innodb 通過間隙鎖是的B連接中  insert into t2 values(27,3) 插入失敗,來消滅幻讀的出現。
但是這種方法是有 局限的,它會將 a=24--29(30-1)中間的任何數都鎖住,所以才叫間隙鎖;
 
B連接中則只能插入不在這個區間的數據;
 
鎖升級

 
  • 由一句單獨的sql語句在一個對象上持有的鎖的數量超過了閾值,默認這個閾值為5000.值得注意的是,如果是不同對象,則不會發生鎖升級。
  • 鎖資源占用的內存超過了激活內存的40%時就會發生鎖升級
 
innodb不存在鎖升級的問題。因為其不是根據每個記錄來產生行鎖的,相反,其根據每個事務訪問的每個頁對鎖進行管理的,采用的是 位圖的方式。因此不管一個事務鎖住頁中一個記錄還是多個記錄,其開銷通常都是一致的。
 
簡單說innodb根據頁進行加鎖,並采用位圖方式,定位到行的,所需資源較小。
例子:
 

 

死鎖

 

 

 
死鎖數據庫自動解決
     數據庫挑選沖突事務中回滾代價較小的事務回滾
 
死鎖預防
     單表死鎖可以根據批量更新表的更新條件排序
     可能沖突的跨表事務盡量避免並發
     盡量縮短事務長度
 
排查死鎖:
  • 了解觸發死鎖的sql所在事務的上下文
  • 根據上下文語句加鎖的范圍來分析存在爭用的記錄
  • 通常改善死鎖的主要方法:
        --對同一表的操作根據加鎖條件進行排序
        --拆分長事務
 
業務邏輯加鎖

 
     業務流程中的悲觀鎖(開始的時候,在所有記錄加鎖,直到最后釋放;而樂觀鎖開始不加鎖,只是在最后提交中看提交有沒有成功,沒成功返回給應用程序)
 
     悲觀鎖開始就給所有記錄加鎖,一般等所有業務流程完成,才釋放鎖;因此會對並發性能有一定的影響;
 
如何縮短鎖的時間?
1)開始的時候讀取要修改的數據,amount(金額)
2)做業務流程
3)在update時,加鎖且判斷,現在的amount和開始的amount是否為一個值,如果是,說明這期間amount為改變,則更新;如果amount值改了,則不更新,交給業務來判斷該怎么做。
 
這樣僅是在update這個語句加鎖,大大的縮短的鎖的時間提高了並發性;
 
但是如果業務十分的繁忙,amount的值在不斷改變,此時這個update 就不斷的失敗,整個事務就不斷的失敗,反而影響了 性能。那么該如何做呢?
 
在開始的時候不讀取數據,等到要提交的時候讀取並加鎖提交;
 
 總結

 
  •  更新丟失
  •  innodb意向鎖:
    • 表鎖
    • 自動施加、自動釋放
    • 為了揭示事務下一行將被請求的鎖類型
  •  S鎖:in share mode
  •  X鎖:for update
  •  innodb行鎖特點:
    • 只有條件走索引才能實現行鎖
    • 索引上有重復值可能鎖住多個記錄
    • 查詢有多個索引可以走,可以對不同索引加鎖
  •  gap lock:間隙鎖,消滅幻讀
  •  死鎖解決:數據庫挑回滾代價較小的事務回滾;
  •  死鎖預防:
    • 單表,更新條件排序
    • 避免跨表事務,縮短事務長度
  •  鎖升級:
    • 單獨sql語句在單個對象的鎖數量超過闕值
    • 鎖資源占用的內存超過了激活內存的40%;
  •  innodb根據頁進行加鎖,並采用位圖方式,定位到行的,所需資源較小

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM