這個博客記錄的是本身學習過程和理解,有些細節可能寫的不見得準確,想了解概念,谷歌能夠搜出不少不錯的博文去學習.我把它記下來,則是爲了記錄學習的過程,由於有些知識是須要本身創造實踐的機會才能真正理解其中的細節.mysql
由ANSI/ISO定義的SQL-92標準定義的四種隔離級別sql
讀取未提交內容/髒讀(READ UNCOMMITTED/Dirty Read):無效數據的讀出 . SELECT語句是在無鎖的模式下運行,一個事務能夠讀到另一個事務的未提交的數據。數據庫
提交讀(READ COMMITTED):一個事務不能讀到另一個事務未提交的數據。在這種隔離級別下,對於更新語句來講(SELECT FOR UPDATE,UPDATE和DELETE),Innodb只會對where條件中索引能覆蓋到的行進行上鎖
,不會上gap鎖和next key lock
,因此就避免不了不可重複讀和幻讀
。session
可重複讀(REPEATABLE READ):是Innodb默認的隔離級別。它使用了gap locks
或者next-key locks
來避免不可重複讀的現象,可是仍然避免不了幻讀
。若是SELECT FOR UPDATE,UPDATE以及DELETE的where條件能用到惟一索引,INNODB只會對惟一索引能覆蓋到的行上行鎖;不然就上gap locks或者Next-key locks。併發
串行化(SERIALIZABLE):這是最高的隔離級別,避免了幻讀。若是auto commits不啓用,則innodb把每一個select轉換成select ... lock in share mode
(使用共享鎖);若是啓用,則每一個SELECT語句也是事務。性能
隔離解別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
Read Uncommitted | Y | Y | Y |
Read Committed | N | Y | Y |
Repeatable(default) | N | N | Y |
Serializable | N | N | N |
一部分概括可能並不許確,望斧正學習
這裏先介紹幾個概念優化
行鎖的一種,共享鎖又稱讀鎖,在對任何數據進行讀操做以前要申請並得到共享鎖 ,若事務1對數據對象A加共享鎖,則事務1能夠讀A但不能修改A
(其餘事務只能再對1加共享鎖,而不能加排他鎖,),其餘事務只能再對A加共享鎖,而不能加排他鎖,直到事務1釋放A上的共享鎖。這保證了其餘事務能夠讀A,但在事務1釋放A上的S鎖以前不能對A作任何修改。 共享鎖的英文名是Share Locks,其實若是叫作讀鎖就好理解多了(畢竟字面上'共享'這個詞好像沒有這東西就不能改的意思嘛) 在查詢語句後面增長LOCK IN SHARE MODE
,Mysql會對查詢結果中的每行都加共享鎖翻譯
session1 | session2 |
---|---|
begin tran<br>select * from table1 holdlock where B='b2'<br>waitfor delay '00:00:30'<br>commit tran | begin tran<br>select A,C from table1 where B='b2'<br>update table1 set A='aa' where B='b2'<br>commit tran |
行鎖的一種,排他鎖又稱寫鎖,互斥鎖,獨佔鎖(名字可真多,但都是一個意思嘛)。在進行寫操做以前要申請並得到排他鎖,若事務1對數據對象A加上排他鎖,事務1能夠讀A也能夠修改A,其餘事務不能再對A加任何鎖,直到1釋放A上的鎖。這保證了其餘事務在1釋放A上的鎖以前不能再讀取和修改A。 排他鎖英文名字是Exclusive Locks(我英語很差,不太明白爲啥叫X鎖不叫E鎖),這個字面好理解,排除其餘的事務,只能本身讀寫,雖然叫寫鎖,實際上事務1 rw的權限都是有的 在查詢語句後面增長FOR UPDATE
,Mysql會對查詢結果中的每行都加排他鎖 對於insert、update、delete,InnoDB會自動給涉及的數據加排他鎖(X)code
兩段鎖協議是指每一個事務的執行能夠分爲兩個階段:生長階段(加鎖階段)和衰退階段(解鎖階段)。
在該階段能夠進行加鎖操做。在對任何數據進行讀操做以前要申請並得到共享鎖,在進行寫操做以前要申請並得到排他鎖。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操做不能再進行加鎖操做。
兩段封鎖法能夠這樣來實現:事務開始後就處於加鎖階段,一直到執行ROLLBACK和COMMIT以前都是加鎖階段
。ROLLBACK和COMMIT使事務進入解鎖階段,即在ROLLBACK和COMMIT模塊中DBMS釋放全部封鎖。
爲了方便檢測表級鎖和行級鎖之間的衝突,減小加鎖時封鎖檢查的工做量,支持多粒度的鎖定,基於兩種基本的鎖類型,能夠派生出以下兩種意向鎖: 概念有點抽象,簡單的一下爲何要意向鎖: 意向鎖是爲了不逐行檢查行是否上鎖,解決行鎖和表鎖之間的矛盾 好比事務1要在一個表上加共享鎖,若是表中的一行已被事務2加了排他鎖,那麼該鎖的申請也應被阻塞。若是表中的數據不少,逐行檢查鎖標誌的開銷將很大,系統的性能將會受到影響。 若是表中記錄1億,事務1把其中有幾條記錄上了行鎖了,這時事務2須要給這個表加表級鎖,若是沒有意向鎖的話,那就要去表中查找這一億條記錄是否上鎖了。若是存在乎向鎖,那麼假如事務1在更新一條記錄以前,先加意向鎖,再加獨佔鎖
,事務2先檢查該表上是否存在乎向鎖
,存在的意向鎖是否與本身準備加的鎖衝突,若是有衝突,則等待直到事務1釋放,而無須逐條記錄去檢測。事務2更新表時,其實無須知道到底哪一行被鎖了,它只要知道反正有一行被鎖了就好了。 意向鎖是InnoDB自動加的,不須要用戶干預
。
事務打算給數據行加共享鎖,事務在給一個數據行加共享鎖前必須先給該表加上IS鎖。
事務打算給數據行加排他鎖,事務在給一個數據行加排他鎖前必須先給該表加上IX鎖。 意向鎖主要目的是爲了在一個事務中揭示下一行將被請求的鎖類型。若是沒意向鎖,須要表鎖時,要一行行檢查某個表是否發生了行鎖,進而判斷可否表鎖成功,若是有了意向鎖,不用一個個去檢查,直接從表的層次就可判斷。
表級鎖
,但表示的是事務正在操做某一行記錄
。衝突檢測是在加行鎖時發生
。對整個表加鎖,影響表的全部記錄。一般用在DDL語句中,如ALTER、DROP、TRUNCATE等。
對一行記錄加鎖(準確的說應該是對索引加鎖而非記錄自己),隻影響一條記錄。一般用在DML語句中,如INSERT、UPDATE、DELETE等。
鎖住某一段範圍中的記錄 ,在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄以前或者以後加鎖,並不包括該索引記錄自己,去解決幻讀
和得到基於語句模式下複製的一致性
是Record lock和gap lock的結合,即除了鎖住記錄自己,還要再鎖住索引之間的間隙 行鎖和GAP鎖結合造成的的Next-Key鎖共同解決了RR級別在寫數據時的幻讀
問題
正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。
在悲觀鎖的狀況下,爲了保證事務的隔離性,就須要一致性鎖定讀。讀取數據時給加鎖,其它事務沒法修改這些數據。修改刪除數據時也要加鎖,其它事務沒法讀取這些數據
相對悲觀鎖而言,樂觀鎖機制採起了更加寬鬆的加鎖機制。悲觀鎖大多數狀況下依靠數據庫的鎖機制實現,以保證操做最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷每每沒法承受。
而樂觀鎖機制在必定程度上解決了這個問題。樂觀鎖,大可能是基於數據版本( Version )記錄機制實現。何謂數據版本?即爲數據增長一個版本標識,在基於數據庫表的版本解決方案中,通常是經過爲數據庫表增長一個 「version」 字段來實現。讀取出數據時,將此版本號一同讀出,以後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據。
要說明的是,多版本併發控制(MVCC)的實現沒有固定的規範,每一個數據庫都會有不一樣的實現方式
這個事務隔離級別任何操做都不會加鎖
在RC級別中,數據的讀取都是不加鎖的,可是數據的寫入、修改和刪除是須要加鎖的,這樣能夠避免髒讀問題 若是where的條件是沒有索引的,存儲引擎層面就會將全部記錄
加鎖後返回,再由MySQL Server層進行過濾。但在實際使用過程中,MySQL作了一些改進,在MySQL Server過濾條件,發現不知足後,會調用unlock_row方法,把不知足條件的記錄釋放鎖 (違背了二段鎖協議的約束)。 雖然mysql作了優化,咱們仍是儘可能使用索引避免鎖住不相關的記錄
舉個栗子:若是事務1和事務2都是插入了同一條記錄,由於行鎖的存在,有一個一定拋異常.雖然拋了異常,但不影響失敗的那個事務獲得了成功事務釋放掉的排它鎖.若是這時不作回滾,這時忽然冒出個事務3 讀取記錄,這就會致使死鎖現象 因此代碼裏若是是由於主鍵衝突拋出了異常,是要作回滾來釋放獨佔鎖的 引用別人的表格加以說明 | session1 | session2 | session3 | | :-- | :-- | :-- | | Session_1得到for update的共享鎖:<br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update;| 因爲記錄不存在,session_2也能夠得到for update的共享鎖:<br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update; | | | Session_1能夠成功插入記錄:<br> mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | | | Session_2插入申請等待得到鎖:<br>mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | Session_1成功提交: <br>mysql> commit; | | | | | Session_2得到鎖,發現插入記錄主鍵重,這個時候拋出了異常,可是並無釋放共享鎖: <br>mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom'); | | | | | Session_3申請得到共享鎖,由於session_2已經鎖定該記錄,因此session_3須要等待: <br>mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update; |
這是MySQL中InnoDB默認的隔離級別。 在同一個事務內的查詢都是事務開始時刻一致的 對選定對象的讀鎖和寫鎖一直保持到事務結束,但不要求「範圍鎖」,所以可能會發生幻讀 帶惟一搜索條件使用惟一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE 和DELETE語句只鎖定找到的索引記錄,而不鎖定記錄前的間隙 用其它搜索條件,這些操做採用next-key鎖定,用next-key鎖定或者間隙鎖定鎖住搜索的索引範圍,而且阻止其它用戶的新插入。 select ... for update 是行級鎖,也是悲觀鎖
每次讀都須要得到表級共享鎖,寫加排他鎖,讀寫互斥。使用的悲觀鎖
簡單的select操做
,沒有lock in share mode或for update,快照讀不會加任何的鎖,並且因爲MySQL的一致性非鎖定讀的機制存在,任何快照讀也不會被阻塞
。可是若是事務的隔離級別是SERIALIZABLE的話,那麼快照讀也會被加上共享的next-key鎖
insert,update,delete,select..in share mode和select..for update,當前讀會在全部掃描到的索引記錄上加鎖
,無論它後面的where條件到底有沒有命中對應的行記錄。當前讀可能會引發死鎖。
事務1:更新一條數據 ------------->事務2:讀取事務1更新的記錄 事務1:調用commit進行提交
名字有點抽象,很差理解,不但是不能仍是不要的意思?爲何不可?我就補充一下主謂賓解釋一下吧 [同一條記錄]不可重[被同一個事務]讀[,由於兩次讀取的結果可能不同] 舉個栗子:
事務1:查詢一條記錄 -------------->事務2:更新事務1查詢的記錄 -------------->事務2:調用commit進行提交 事務1:再次查詢上次的記錄
由於中間事務2作了改動,致使事務1查兩次的結果是不同的 不可重複讀重點在於update和delete
[同一條記錄]可重[被同一個事務]讀[,由於不管多少次結果都是同樣滴]
名字有點抽象,翻譯過來叫作"幻想讀"(有點像遊戲技能名字) 在事務執行過程當中,當兩個徹底相同的查詢語句執行獲得不一樣的結果集。這種現象稱爲幻讀 舉個栗子:小時候看 《哈克貝利·費恩歷險記》記得有個小橋段,薩萊姨媽數叉子個數時湯姆和哈克趁她不注意放回或拿走叉子,讓薩萊姨媽每次數出來叉子的個數都不同.幻讀基本就是這個意思,仍是用文字表示一下這現象是怎麼發生的: 事務1:查詢表中全部記錄 -------------->事務2:插入一條記錄 -------------->事務2:調用commit進行提交 事務1:再次查詢表中全部記錄 因爲事務2(湯姆和哈克)偷摸加了一條記錄(叉子),結果致使事務1(薩萊姨媽)數出的個數和上次的比多了一個,結果就讓她直接懵了 幻讀的重點在於insert