語句順序\\事務 | 事務一 | 事務二T1 | begin; | begin; | T2 | select * from student where id = 1 for update; | select * from student where id = 2 for update; | T3 | select * from student where id = 2 for update; | T4(死鎖發生) | select * from student where id = 1 for update;"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是最簡單最典型的死鎖的情況了,兩個事務互相鎖定持有資源,並且等待對方的資源,最後形成一個環,死鎖出現。最後某個事務回滾,寫業務代碼的時候,應該對併發條件可能出現這種情況的語句有所警覺。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"死鎖例二"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前提:事務開始時,student表裏有id=1的記錄"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":" |
|
---|
MySQL鎖系統總結
{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"innoDB鎖簡介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"innoDb支持多種粒度的鎖,按照粒度來分,可分爲"},{"type":"codeinline","content":[{"type":"text","text":"表鎖(LOCK_TABLE)"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"行鎖(LOCK_REC)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般的鎖系統都會有"},{"type":"codeinline","content":[{"type":"text","text":"共享鎖"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"排他鎖"}]},{"type":"text","text":"的分類,"},{"type":"codeinline","content":[{"type":"text","text":"共享鎖"}]},{"type":"text","text":"也叫"},{"type":"codeinline","content":[{"type":"text","text":"讀鎖"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"排他鎖"}]},{"type":"text","text":"也叫"},{"type":"codeinline","content":[{"type":"text","text":"寫鎖"}]},{"type":"text","text":"。加在同一個資源上,寫鎖會阻塞另外一把寫鎖或讀鎖的獲取,讀鎖則允許另外一把讀鎖的獲取,也就是讀讀之間允許併發,讀寫或者寫寫會阻塞,innodb中表鎖和行鎖都支持"},{"type":"codeinline","content":[{"type":"text","text":"共享鎖(簡寫S)"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"排他鎖(簡寫X)"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲innoDB支持多粒度的鎖,允許表鎖和行鎖的並存,爲了方便多粒度鎖衝突的判斷,innoDB中還存在一種名叫"},{"type":"codeinline","content":[{"type":"text","text":"意向鎖(Intention Locks)"}]},{"type":"text","text":"的鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除此之外,還有一種特殊的表鎖,自增鎖,主要用來併發安全的生成自增id,一種特殊的意向鎖,插入意向鎖,用來防止幻讀問題"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"表鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"表鎖,鎖定的粒度是整個表,也分共享鎖和排他鎖。不同於行鎖,表鎖MySQL Server層就有實現(所以MyISAM支持表鎖,也只支持表鎖),innoDb則在存儲引擎層面也實現了一遍表鎖(後面會介紹具體結構)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"哪些時候會觸發表鎖呢?在執行某些ddl時,比如"},{"type":"codeinline","content":[{"type":"text","text":"alter table"}]},{"type":"text","text":"等操作,會對整個表加鎖,也可以手動執行鎖表語句:"},{"type":"codeinline","content":[{"type":"text","text":"LOCK TALBES table_name [READ | WRITE]"}]},{"type":"text","text":",READ爲共享鎖,WRITE爲排他鎖,手動解鎖的語句爲:"},{"type":"codeinline","content":[{"type":"text","text":"UNLOCK TABLES"}]},{"type":"text","text":",會直接釋放當前會話持有的所有表鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有一些需要注意的地方:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲MySQL Server層和InnoDB都實現了表鎖,僅當"},{"type":"codeinline","content":[{"type":"text","text":"autocommit=0"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"innodb_table_lock=1"}]},{"type":"text","text":"(默認設置)時,InnoDB層才能知道MySQL加的表鎖,MySQL Server才能感知InnoDB加的行鎖,這種情況下,InnoDB才能自動識別涉及表級鎖的死鎖,否則,InnoDB將無法自動檢測並處理這種死鎖"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在用"},{"type":"codeinline","content":[{"type":"text","text":"LOCK TALBES"}]},{"type":"text","text":"顯式獲取鎖後,只能訪問加鎖的這些表,並且如果加的是共享鎖,那麼只能執行查詢操作,而不能執行更新操作,如果獲得的是排他鎖,則可以進行更新操作。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開始事務時會自動UNLOCK之前的表鎖,COMMIT或ROLLBACK都不能釋放用"},{"type":"codeinline","content":[{"type":"text","text":"LOCK TABLES"}]},{"type":"text","text":"加的表級鎖。"},{"type":"codeinline","content":[{"type":"text","text":"LOCK TALBES"}]},{"type":"text","text":"時會先隱式提交事務,再鎖表,"},{"type":"codeinline","content":[{"type":"text","text":"UNLOCK TALBES"}]},{"type":"text","text":"也會隱式提交事務。所以,事務中需要的表鎖必須在事務開頭一次性獲取,無法再事務中間獲取,因爲不管是"},{"type":"codeinline","content":[{"type":"text","text":"LOCK TALBES"}]},{"type":"text","text":"還是"},{"type":"codeinline","content":[{"type":"text","text":"UNLOCK TALBES"}]},{"type":"text","text":"都會提交事務"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"官網上建議的表鎖的使用方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"SET autocommit=0;\nLOCK TABLES t1 WRITE, t2 READ, ...;\n... do something with tables t1 and t2 here ...\nCOMMIT;\nUNLOCK TABLES;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際業務中,沒有特殊理由,不建議使用表鎖,因爲鎖的粒度太大了,極大的影響併發。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"意向鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"意向鎖是一種特殊的表級鎖,意向鎖是爲了讓InnoDB多粒度的鎖能共存而設計的。取得行的共享鎖和排他鎖之前需要先取得表的意向共享鎖(IS)和意向排他鎖(IX),意向共享鎖和意向排他鎖都是系統自動添加和自動釋放的,整個過程無需人工干預。 主要是用來輔助表級和行級鎖的衝突判斷,因爲Innodb支持行級鎖,如果沒有意向鎖,則判斷表鎖和行鎖衝突的時候需要遍歷表上所有行鎖,有了意向鎖,則只要判斷表是否存在意向鎖就可以知道是否有行鎖了。表級別鎖的兼容性如下表:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ba\/ba9e6a9da8feda1a02e2e377c1caace5.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖片可以看到,意向鎖與意向鎖兼容,IX、IS自身以及相互都兼容,不互斥,因爲意向鎖僅表示下一層級加什麼類型的鎖,不代表當前層加什麼類型的鎖;IX和表級X、S互斥;IS和表級X鎖互斥。其兼容性正好體現了其作用。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"自增鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自增鎖是一種特殊的表級別鎖,如果一個表的某個行具有AUTO_INCREMENT的列,則一個事務在插入記錄到這個表的時候,會先獲取自增鎖。如果一個事務持有自增鎖,會阻塞其他事物對該表的插入操作,保證自增連續。innodb_autoinc_lock_mode變量定義了不同的自增算法,在MySql8.0之前默認值是1,MySql8.0之後默認值是2,具體區別參考官方文檔(https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/innodb-auto-increment-handling.html)"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"行鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Innodb中的行鎖種類繁多,可以分爲:記錄鎖(record locks)、間隙鎖(gap locks)、臨鍵鎖(next-key locks),插入意向鎖(insert intention locks)。行鎖在邏輯上都可以看作作用於索引或者索引間隙之上,索引分爲主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,MySQL就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,MySQL會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多語句都會加行鎖,比如Update、Delete、Insert等操作,或者使用SELECT ... FOR SHARE | UPDATE [NOWAIT |SKIP LOCKED]來進行當前讀(Locking Reads),其中SHARE表示加共享鎖,UPDATE表示加排他鎖。當要加的鎖與當前行已有鎖互斥時,會一直阻塞等待一段時間(innodb_lock_wait_timeout定義了等待時間)。加上NOWAIT參數則不會阻塞,會立即返回,並顯示一個錯誤,加上SKIP LOCKED則會在結果集中跳過這些衝突的記錄(慎用)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在不同的語句,不同的事務隔離級別下,甚至不同的索引類型下,行鎖會表現成不同的形式,下面介紹這些形式:"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"記錄鎖(record locks)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在邏輯上,記錄鎖可以理解爲鎖定的是某個具體的索引,當SQL執行按照唯一性(Primary key、Unique key)索引進行數據的檢索時,查詢條件等值匹配且查詢的數據是存在,這時 SQL 語句加上的鎖即爲記錄鎖。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"圖片間隙鎖(gap locks)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在邏輯上,間隙鎖可以理解爲鎖住的是索引之間的間隙,是一個左開右開的區間。當SQL執行按照索引進行數據的檢索時,查詢條件的數據不存在,這時SQL語句加上的鎖即爲間隙鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖片如上圖,因爲這些語句查詢的值都不存在,所以鎖住的都是間隙。並且在 InnoDb 存儲引擎裏,每個數據頁中都會有兩個虛擬的行記錄,用來限定記錄的邊界,分別是:Infimum Record 和 Supremum Record,Infimum 是比該頁中任何記錄都要小的值,而 Supremum 比該頁中最大的記錄值還要大,這兩條記錄在創建頁的時候就有了,並且不會刪除。所以當查詢的值比當前已有記錄最大值還大時候,鎖住的會是最大值到Supremum之間的間隙。比如第一條語句,查詢的時候就算是等值匹配,只要這個不存在的數據落在兩個索引節點之間,就算不是一個範圍,也會鎖住索引節點間的所有數據即gap3,範圍(7,11)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"間隙鎖是可以共存的,共享間隙鎖與獨佔間隙鎖之間是沒有區別的,兩者之間並不衝突。其存在的目的都是防止其他事務往間隙中插入新的紀錄,故而一個事務所採取的間隙鎖是不會去阻止另外一個事務在同一個間隙中加鎖的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"間隙鎖是設計用來防止幻讀的,當鎖定一個gap時,其他事務沒有辦法再往這個gap中插入數據,PostgreSQL沒有這種機制,所以PostgreSQl沒有辦法鎖住不存在的行,無法防止幻讀"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"臨鍵鎖(next-key locks)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在邏輯上,臨鍵鎖可以理解爲鎖住的是索引本身以及索引之前的間隙,是一個左開右閉的區間。當SQL執行按照非唯一索引進行數據的檢索時,會給匹配到行上加上臨鍵鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖片如上圖,當執行select * from table_name where id = 3 for update時會鎖定(-∞,3)區間,因爲按照這個SQL的語義,即是爲了鎖住id=3的數據,不允許其他操作,如果只是鎖住記錄本身,肯定是沒有辦法保證的,因爲這是非唯一索引,還有可能插入其他id=3的數據,如果把間隙都給鎖住,則其他對這個間隙的插入操作都會被阻塞,從而保證了一致性,這也是臨鍵鎖的用意。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果加鎖時,查詢條件沒有命中索引(非ICP的查詢),則InnoDB會嘗試給全表每一條記錄都加上臨鍵鎖,效果相當於鎖表了"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"插入意向鎖(insert intention locks)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入意向鎖是一種間隙鎖形式的意向鎖,在真正執行INSERT操作之前設置。當執行插入操作時,總會檢查當前插入操作的下一條記錄(已存在的主索引節點)上是否存在鎖對象,判斷是否鎖住了gap,如果鎖住了,則判定和插入意向鎖衝突,當前插入操作就需要等待,也就是配合上面的間隙鎖或者臨鍵鎖一起防止了幻讀操作。 因爲插入意向鎖是一種意向鎖,意向鎖只是表示一種意象,所以插入意向鎖之間不會互相沖突,多個插入操作同時插入同一個gap時,無需互相等待,比如當前索引上有記錄4和8,兩個併發session同時插入記錄6,7。他們會分別爲(4,8)加上GAP鎖,但相互之間並不衝突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"INSERT語句在執行插入之前,會先在gap中加入插入意向鎖,如果是唯一索引,還會進行Duplicate Key判斷,如果存在相同Key且該Key被加了互持鎖,則還會加共享鎖,然後等待(因爲這個相同的Key之後有可能會回滾刪除,這裏非常容易死鎖)。等到成功插入後,會在這條記錄上加排他記錄鎖。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"行鎖小結"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/43\/43f71cbb3cbb29b4206ad9ae1d3f3932.png","alt":"圖片","title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"行鎖在不同的語句中和環境條件下可以表現成:記錄鎖(record locks)、 間隙鎖(gap locks)、臨鍵鎖(next-key locks)和插入意向鎖(insert intention locks)。記錄鎖鎖住具體的記錄,間隙鎖鎖住記錄之間的間隙,臨鍵鎖鎖住記錄和記錄前面的間隙,插入意向鎖則是特殊的間隙鎖,在插入前判斷行將要插入的間隙是否會有衝突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上說的各種行鎖的加鎖情況都是在可重複讀(REPEATABLE READ)隔離級別下,這個級別也是innoDB默認的事務隔離級別,是最常用的隔離級別,但是其實不同語句在不同隔離級別下加鎖的情況會有非常大的區別,以下會簡單說明。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"不同語句和隔離級別對加鎖的影響"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏先排除讀未提交(READ UNCOMMITTED)這種隔離級別的情況,這種級別在生產上幾乎無法使用,會出現髒讀的情況,不一致讀,無法保證事務的ACID。然後先看下串行化(SERIALIZABLE)隔離級別。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"串行化隔離級別和可重複讀隔離級別最大的區別應該是,innoDB會隱式的轉換所有的SELECT語句,給其加共享鎖,變成SELECT ... FOR SHARE,這樣讀操作會阻塞其他寫操作,使得讀寫無法併發,只能串行,從而保證嚴格的一致性。不過這種行爲也受到autocommit變量的影響:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果禁用了autocommit,如上所述,則innoDB會隱式的轉換所有的SELECT語句,給其加共享鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果開啓了autocommit,不會進行隱式轉換,因爲每條語句構成一個事務,所有快照讀語句(也就是沒有FOR UPDATE|SHARE的SELECT語句)可以被認爲是隻讀的事務,是可以安全併發,不需要阻塞其他事務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可重複讀(READ COMMITTED)隔離級別下,和可重複讀隔離級別在行鎖方面主要的區別是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可重複讀隔離級別下取消了間隙鎖,臨鍵鎖也退化成了記錄鎖。間隙鎖定和臨鍵鎖僅用於外鍵約束檢查和重複鍵檢查,也就是說加鎖時,如果沒有符號條件的查詢並不加鎖,有符合條件的查詢也只會給記錄加上記錄鎖。因爲沒有了間隙鎖,所以會出現幻讀問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在可重複讀隔離級別下,加鎖時如果查詢條件沒有命中索引(非ICP的查詢),則會給表中每條記錄都加上臨鍵鎖。而不可重複讀隔離級別下因爲沒有間隙鎖,則會退化成給表中每條數據加上記錄鎖,並且還會把沒有匹配的行上的鎖給釋放掉,而不是把全表所有記錄不管有沒有匹配都給鎖上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"7死鎖因爲使用表鎖時,需要一次性申請所有所需表的鎖,所以在只使用表鎖的情況下不會出現死鎖,一般出現死鎖的情況都是行鎖。innoDB有死鎖探測機制,在申請鎖的時候,都會先進行死鎖判斷,採用的算法深度優先搜索,並且如果在搜索過程中發現有環,就說明發生了死鎖,爲了避免死鎖檢測開銷過大,如果搜索深度超過了 200(LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK)也同樣認爲發生了死鎖。出現死鎖時,innoDB會選擇一個回滾代價比較小的事務進行回滾。以下會舉幾個比較典型的死鎖例子(均在可重複度隔離級別下),首先會先建一張測試的表:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"CREATE TABLE `student` (\n`id` int NOT NULL,\n`uuid` varchar(64) NOT NULL,\n`name` varchar(64) NOT NULL,\n`age` int NOT NULL,\nPRIMARY KEY (`id`),\nUNIQUE KEY `uuid_index` (`uuid`),\nKEY `name_index` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"死鎖例一"}]},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.