此次咱們只談數據庫。以往遇到併發事務問題,總想着站在應用層面考慮問題,該如何實現悲觀鎖和樂觀鎖?但並不很清楚數據庫中底層的實現原理。找幾個晚上從新溫習了一下大學課堂上的《數據庫系統概覽》,借這篇文章記下來此次讀書的概要。mysql
若是想要說明一個數據庫或者一個框架支持事務性操做,則必需要知足下面的四大特性。sql
在數據庫事務的ACID四個屬性中,隔離性是一個最常放鬆的一個。能夠在數據操做過程當中利用數據庫的鎖機制或者多版本併發控制機制獲取更高的隔離等級。可是,隨着數據庫隔離級別的提升,數據的併發能力也會有所降低。因此,如何在併發性和隔離性之間作一個很好的權衡就成了一個相當重要的問題。數據庫
實際開發過程當中,咱們絕大部分的事務都是有併發狀況。當多個事務併發運行,常常會操做相同的數據來完成各自的任務。在這種狀況下可能會致使如下的問題:併發
不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。oracle
ANSI/ISO SQL定義的標準隔離級別有四種,從低到高依次爲:未提交讀(Read uncommitted) < 提交讀(Read committed) < 可重複讀(Repeatable reads) < 序列化(Serializable)。框架
mysql:默認隔離級別是 Repeatable Read,會出現幻讀的問題。oracle/sql server:默認隔離級別是 Read Committed,會出現不可重複讀和幻讀的問題。spa
產生幻讀的緣由是,行鎖只能鎖住行,可是新插入記錄這個動做,要更新的是記錄之間的「間隙」。所以,爲了解決幻讀問題,mysql InnoDB 只好引入新的鎖,也就是間隙鎖 (GapLock)。間隙鎖,鎖的就是兩個值之間的空隙。值得注意的是,間隙鎖只在隔離級別是Repeatable read 下才會生效。線程
默認狀況下,InnoDB工做在"可重複讀"的隔離狀況下,而且以Next-Key Lock的方式對數據進行加鎖,這樣就能夠有效地防止"幻讀"的發生。Next-key Lock是行鎖與間隙鎖的組合,這樣,當InnoDB掃描索引記錄的時候,會首先對選中的索引記錄加上行鎖(Record Lock),再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。若是一個間隙被事務T1加了鎖,其餘事務是不能在這個間隙插入記錄的。例以下面的sql,會對id取值範圍大於100的間隙加鎖。當增刪數據庫時若是id大於100則會阻塞住,若是小於100則不會有影響。code
select * from emp where id > 100 for update;
鎖是數據庫系統區別於文件系統的一個關鍵特性,鎖機制用於管理對共享資源的併發訪問。粗略的分有行級鎖、表級鎖和頁級鎖,行級鎖再分共享鎖和排他鎖等。要記住悲觀鎖和樂觀鎖是兩個抽象的概念,並不是實際數據庫中的鎖。server
共享鎖(S鎖):容許一個事務去讀一行,會阻止其餘事務獲取相同數據集的排他鎖(讀取數據的時候不容許修改),也被稱爲讀鎖。當沒有其餘線程對查詢結果集中的任何一行使用排他鎖時,能夠成功申請共享鎖,不然會被阻塞。其餘線程也能夠讀取使用了共享鎖的表,並且這些線程讀取的是同一個版本的數據。
mysql的共享鎖支持行級鎖,在查詢語句後面增長LOCK IN SHARE MODE,mysql會對查詢結果中的每行都加共享鎖。
SELECT ... LOCK IN SHARE MODE;
oracle的共享鎖不支持行級鎖,只能是表級鎖。
LOCK TABLE <表名>[,<表名>]... IN SHARE MODE [NOWAIT]
有一些誤區須要記住,單純的查詢SELECT不是共享鎖,其實是沒有上任何鎖。而 SELECT ... FOR UPDATE 是排他鎖。
排他鎖(X鎖):容許獲取排他鎖去作更新操做,阻止其餘事務獲取相同數據的共享鎖和排他鎖(一個事務修改數據的時候,阻止其餘事務對相同數據集作更新或者查詢操做),也被稱爲寫鎖。
排他鎖的使用方式比較一致,新增、更新和刪除等DML操做語句會自動加上排他鎖。並且SELECT ... FOR UPDATE 也是排他鎖。
爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖:
意向共享鎖(IS):事務打算給數據行加共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加排他鎖,事務再給一個數據行加排他鎖前必須先取得該表的IX鎖。
意向鎖是數據庫隱式幫咱們作了,咱們不須要操心。
悲觀鎖:是從數據庫層面加鎖。老是假設最壞的狀況,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它釋放鎖。
樂觀鎖:老是假設最好的狀況,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據。
那麼在數據庫層面上,咱們利用鎖來實現事務的,不管是共享鎖仍是排他鎖,都是悲觀鎖。而樂觀鎖是經過業務來實現,並不會利用到數據庫的鎖。