1. 程式人生 > >關於InnoDB的讀寫鎖型別以及加鎖方式

關於InnoDB的讀寫鎖型別以及加鎖方式

(本文為了方便,英文關鍵詞都都採用小寫方式,相關知識點會簡單介紹,爭取做到可以獨立閱讀)

文章開始我會先介紹本文需要的知識點如下:

  • innodb的聚簇索引(聚集索引)和非聚簇索引(二級索引、非聚集索引)的知識
  • innodb的隔離級別isolation level
  • 簡單的sql知識(能讀懂sql語句)
  • MVCC(Multi-Version Concurrent Control)多版本併發控制
  • 資料的髒讀、幻讀(如果有時間會詳細講一下髒讀如果沒時間,網上講這個地方的也很多)
問題1:讀有幾種模式、加鎖有幾種方式
我們先看一個mysql表和幾條語句表名稱:my_table  搜尋引擎:innodb表結構:
1. select * from my_table where id = 1;
2. select * from my_table where id = 1 lock in share mode;
3. select * from my_table where id = 1 for update;
4. update my_table set address = 'tianjin' where id = 1;
先說隔離級別,mysql隔離級別分為四種:未提交讀(read uncommitted)、提交讀(read committed)、重複讀(repeatable read)、序列化(serializable其中mysql預設的隔離級別重複讀(repeatable read
),以下簡稱為rr,本文也只介紹這種模式
讀的模式分為兩種:
  • 快照讀(snapshot read)
  • 當前讀(current read)
我們先來了解一下MVCC:MVCC是為了實現資料庫的併發控制而設計的一種協議。與其相對的事LBCC即基於鎖的併發控制(Lock-Based Concurrent Control)。要實現資料庫的併發訪問控制,最簡單的做法就是加鎖訪問,即讀的時候不能寫(這個讀為當前讀,後面介紹。允許多個執行緒同時對想讀的內容加鎖,即共享鎖或叫S鎖),寫的時候不能讀(只能有一個執行緒對同一內容進行寫操作,即排它鎖,X鎖)。這樣的加鎖訪問,其實並不算是真正的併發,或者說它只能實現併發的讀,既讀寫序列化,這樣就大大降低了資料庫的讀寫效能。LBCC是四種隔離級別中級別最高的Serialize隔離級別。
MVCC對比LBCC它的最大好處便是,讀不加鎖,讀寫不衝突。在MVCC中,讀操作可以分成兩類,快照讀(Snapshot read)和當前讀(current read)。快照讀,讀取的是記錄的可見版本(可能是歷史版本,即最新的資料可能正在被當前執行的事務併發修改),不會對返回的記錄加鎖,如上面的sql語句1;而當前讀,讀取的是記錄的最新版本,並且會對返回的記錄加鎖,保證其他事務不會併發修改這條記錄。如上面的sql語句2,3,4。不同的是2加的是s鎖,3、4加的是x鎖,insert加的也是x鎖。注:MVCC只在RC和RR兩個隔離級別下工作,其他兩個隔離級別都和MVCC不相容
加鎖的方式:(未涉及意向鎖)
先看一個sql語句
update my_table set name ='zhang' where id = 1;
假設id為主鍵:此條sql執行的時候會給此行資料加x鎖,如下圖

mysql的innodb預設的隔離模式為RR模式,既可重複讀,Innodb的RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的範圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),因此不存在幻讀現象。但是標準的RR只能保證在同一事務中多次讀取同樣記錄的結果是一致的,而無法解決幻讀(不保證在事務中出現)問題。Innodb的幻讀解決是依靠MVCC的實現機制做到的。其他模式以後有時間再加在這裡不對其他模式做講解。這裡因為id為主鍵,innodb中在主鍵上存在聚簇索引,其他的索引均為二級索引,這裡做一下簡單介紹聚簇索引:在innodb儲存引擎中,主鍵的存在至關重要,及時你不為表設定主鍵,儲存引擎也會隱式的定義一個主鍵,只是對使用者來說透明。之所以說他重要,是因為聚簇索引的儲存是和資料儲存在一起的,而聚簇索引的資料就是資料儲存的順序。如果需要查詢的資料是連續的,那麼按照聚簇索引查詢到的資料位置也是連續的,只需要按順序讀取就可以。對於聚集索引,葉子結點即儲存了真實的資料行,不再有另外單獨的資料頁(這裡和後面的二級索引有區別,二級索引的葉子節點指向資料頁資料行的邏輯指標,需要按照指標再去檢索資料)。 在一張表上最多隻能建立一個聚集索引,因為真實資料的物理順序只能有一種。二級索引:表資料儲存順序與索引順序無關。對於非聚集索引,葉結點包含索引欄位值及指向資料頁資料行的邏輯指標,其行數量與資料錶行資料量一致。看上面的sql語句,或者看之前的幾條sql,這個語句執行的時候會給這條記錄加x鎖,這時候如果其他事務中的語句也在進行鎖的操作(既更新、插入或者刪除,以及2語句當前讀操作加的s鎖)就會造成鎖爭用(innodb出現鎖爭用的時候處理方式為回滾超時獲取不到鎖的事務)。我們先按照上面四條語句兩條併發時的相互影響的情況來情況1:id為主鍵
1. select * from my_table where id = 1;
2. select * from my_table where id = 1 lock in share mode;
我們上面說過,語句1為快照讀,對其他的讀或者寫沒有影響。所以這兩條語句並行時,1讀快照,2為語句加s鎖。
select * from my_table where id = 1 lock in share mode;
情況2:id為主鍵
2. select * from my_table where id = 1 lock in share mode;
3. select * from my_table where id = 1 for update;
其中語句2加s鎖,3加x鎖(在資料被加s鎖的時候,其他的給這條想要讀取這條記錄也需要給這條記錄加s鎖,這就是為什麼s鎖是共享鎖。此時是不允許再給這條記錄加x鎖的)兩種鎖是不能同時存在在一條記錄上的。所以兩條語句不能併發執行。情況3:id為主鍵
3. select * from my_table where id = 1 for update;
4. update my_table set address = 'tianjin' where id = 1;
這種情況下兩條語句都需要給資料加x鎖,所以顯然不能併發執行。

下面我們來討論一下id不為主鍵的情況

id若不為主鍵,則不能使用聚簇索引,而在innodb中有一下幾種情況
  • 二級唯一索引
  • 二級不唯一索引
  • 沒有索引
由於只要不是快照讀則一定會加鎖,我們已經瞭解了鎖的形式,則不難明白不論是先加x鎖還是s鎖哪一種,都一定不能再加另一種鎖,所以我們下面只分析加鎖的方式情況4:假設id為二級唯一索引(unique)
4. update my_table set address = 'tianjin' where id = 1;
這裡很明顯需要加x鎖,但是這裡的加鎖和id為主鍵(索引為聚簇索引)的情況加鎖不完全一樣,會稍微複雜一點這個時候我們需要對索引知識有一定的瞭解,上面說過二級索引中的葉子節點儲存的除了索引資訊還有到實際資料的邏輯指標,也就是說我們需要現在二級唯一索引中查詢到這條記錄的邏輯指標,然後通過指標去查詢到資料實際的儲存位置並給這條資料加鎖。注意,這裡的加鎖應該是加在了索引上和資料本身上(或者說是聚簇索引上也可以,因為兩者是儲存在一個結構中的,而innodb中二級索引葉子中的邏輯指標並不是資料儲存的實體地址,而是主鍵值)而不只是二級唯一索引上。如果想用好innodb,他的索引結構是必須要學習的,如果以後有時間會詳細介紹。情況5:age為二級非唯一索引,id為主鍵
5. update my_table set address = 'tianjin' where age = 25;
此種情況比前一種情況更特殊,因為情況3和4都只能找到一條記錄,只需要對這條記錄加鎖,則不會發生結果集被修改的情況。但是如果age為二級非唯一索引,我們看到如下表格中有兩條記錄age=25
如果我們在update的過程中,有一個使用者插入了一條age也為25的資料,那麼就是發生一種現象,你明明更新了所有的age=25的資料,但是執行完了卻有一條資料沒有更新的幻覺,這就是幻讀(可以自行查詢資料,避免本文過長)。這個時候顯然只給查找出的資料加鎖是解決不了這個問題的。所以就有了gap鎖(間隙鎖字面上可能更好理解)這裡需要畫圖大家理解一下:
如圖這裡在age為25的有兩個 ,id分別為1和3。我們在修改執行上面語句的時候,如果沒有gap鎖,則可能發生一種情況:另一個事務執行如下語句
update my_table set age=25 where id=2;
則發生幻讀現象。gap鎖可以防止在語句或者事務執行過程中有滿足條件的記錄插入進來造成幻讀。所以說在此種情況下,除了給滿足條件的二級索引和資料(或聚簇索引)加x鎖之外還要給相關的間隙加鎖。可以理解為這個加gap鎖是在二級索引的範圍內防止新的索引項加入,因為二級索引本身也是有序的。

情況6:age上無索引,id為主鍵
5. update my_table set address = 'tianjin' where age = 25;
這種情況下,所有記錄都被加上了X鎖,每條記錄間的間隙(GAP)也同時被加上了GAP鎖。在實際情況中可能sql更復雜,如果用到了其他列索引,mysql會按照索引順序篩選結果,也許並不會進行全表加x鎖和gap鎖。

相關推薦

關於InnoDB型別以及方式

(本文為了方便,英文關鍵詞都都採用小寫方式,相關知識點會簡單介紹,爭取做到可以獨立閱讀)文章開始我會先介紹本文需要的知識點如下:innodb的聚簇索引(聚集索引)和非聚簇索引(二級索引、非聚集索引)的知識innodb的隔離級別(isolation level)簡單的sql知識

NIO文件並

lec inter string trace acc 加鎖 target ioe stringbu 一、讀取文件 1 package lock; 2 3 import java.io.File; 4 import java.io.FileNotFoundExce

MySQL的死系列- 型別以及原理

疫情期間在家工作時,同事使用了 insert into on duplicate key update 語句進行插入去重,但是在測試過程中發現了死鎖現象: ``` ERROR 1213 (40001): Deadlock found when trying to get lock; try restartin

關於InnoDB類型以及方式

索引 不完全 b2c text 按順序 net c中 加鎖 並不是 (本文為了方便,英文關鍵詞都都采用小寫方式,相關知識點會簡單介紹,爭取做到可以獨立閱讀) 文章開始我會先介紹本文需要的知識點如下: innodb的聚簇索引(聚集索引)和非聚簇索引(二級索引、非聚集索引

java多線程一個變量需要嗎?

多線程 final關鍵字 一個 ati 關鍵字 java多線程 其他 同時 關聯 如果只是讀操作,沒有寫操作,則可以不用加鎖,此種情形下,建議變量加上final關鍵字; 如果有寫操作,但是變量的寫操作跟當前的值無關聯,且與其他的變量也無關聯,則可考慮變量加上volat

InnoDB 的行模式及方法

InnoDB 實現了以下兩種型別的行鎖。  共享鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖。  排他鎖(X):允許獲得排他鎖的事務更新資料,阻止其他事務取得相同資料集的共享 讀鎖和排他寫鎖。 另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB 還有兩種內部使用的

InnoDB的行模式及方法

InnoDB實現了以下兩種型別的行鎖。 l  共享鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖。 l  排他鎖(X):允許獲得排他鎖的事務更新資料,阻止其他事務取得相同資料集的共享讀鎖和排他寫鎖。 另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,Inno

(Read-Write)實現

大部分情況下,使用一個數據結構時並不會對其進行修改。而是隻需要一個區段的讀取許可權來完成工作。如果有多個執行緒需要讀取某一個數據,沒有理由不讓它們併發的進行讀取。Spinlock 鎖無法區分只有讀以及讀寫混合的場景,因為 spinlock 鎖無法滿足這種潛在

黑馬程式設計師一單例設計模式的餓漢式與懶漢式以及的情況

------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">j

STL容器是執行緒不安全的----以及實現多執行緒訪問安全

STL的執行緒安全. 說一些關於stl容器的執行緒安全相關的話題。一般說來,stl對於多執行緒的支援僅限於下列兩點:(貌似Effective STL中有描述)1.多個讀取者是安全的。即多個執行緒可以同時讀取一個容器中的內容。 即此時多個執行緒呼叫 容器的不涉及到寫的介面都可以

Oracle檢視正在執行的SQL,當前被物件以及物件

1、檢視正在執行的SQL SELECT b.sid, b.username, b.serial#, spid, paddr, sql_text,--正在執行的SQL, b.machine --計算機名 FROM

Python學習筆記系列——文件以及敏感詞過濾器的實現

pict user tro users 創建 desktop enc 重建 文件 一、讀文件 #打開文件,傳入文件名和標識符,r代表讀 f= open(‘\\Users\ZC\Desktop\zc.txt‘,‘r‘) #調用read方法一次性讀取文件的全部內容,存

還是不,這是一個問題

https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513692&idx=1&sn=ef2416a4bb96d64db77e32d5b4c7967e&chksm=80d67a9fb7a1f

單例模式與不例項C++

1 教科書裡的單例模式   我們都很清楚一個簡單的單例模式該怎樣去實現:建構函式宣告為private或protect防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動作由一個public的類方法代勞,該方法也返回單例類唯一的例項。

Android 使用資料庫操作應用、未,列表展示效果

效果圖: 要求:1.獲取應用並展示,上下滑動帶動畫 2.未加鎖中點選"鎖"圖示動畫刪除該條目,並新增至 程式鎖 資料庫(存放已加鎖應用) 3.已加鎖中點選"鎖"圖示動畫刪除該條目,並將當前應用從  程式鎖  中刪除 上程式碼: 首先編寫頁面: activity_main.

Java建立以及xml文件(dom方式

package myXML; import org.w3c.dom.*; import org.xml.sax.*; import java.io.*; import javax.xml.parsers.*; import javax.xml.transform.*;

關於properties配置檔案,追加以及中文亂碼問題

在開發中常用properties檔案來儲存系統配置資訊,下面就properties檔案的讀寫,資訊追加作簡要介紹,順便也解決亂碼問題。 1、首先介紹一下properties類 properties類繼承自Hashtable package com.gmi.

ContentProvider聯絡人列表以及許可權處理

ContentProvider簡介: 一個內容提供者訪問資料的中央資源庫。提供者是應用程式的一部分,提供自己的操作資料的UI。然而,內容提供者主要是被其他應用程式引用,通過提供者客戶物件訪問提供者。提供者和提供者客戶端為資料提供一個一致的,標準的介面,也處理程

dom4jxml檔案以及遇到的亂碼解決辦法

這是碰到亂碼問題之後再csdn上看到的: xml亂碼的問題在於編碼集的衝突。   /*//document物件在記憶體中是以"UTF-8"編碼形式存在,用FileWriter將document物件以字元流的形式寫入xml文件預設是用本地碼錶"gb2312"編碼    * 亂

MySQL死系列-常見場景分析

在上一篇文章[《鎖的型別以及加鎖原理》](https://mp.weixin.qq.com/s/QVEUIfD0RBbtvUDORaz2vQ)主要總結了 MySQL 鎖的型別和模式以及基本的加鎖原理,今天我們就從原理走向實戰,分析常見 SQL 語句的加鎖場景。瞭解了這幾種場景,相信小夥伴們也能舉一反三,靈活地