天天看點

一篇就夠了系列:事務隔離級小結

事務是并發操作的副産品,就是描述并發造成的各種與預期不一緻的幾個場景。

如果想又好又快,就往下看喽

友善老手速讀:

一篇就夠了系列:事務隔離級小結

一、 什麼是事務【事務的一些标簽/特點】 (這段隻是為了文章的完整性,老手可跳過)

一篇就夠了系列:事務隔離級小結

度量事務的四個次元:

(1) 原子性(Atomicity):事務是資料庫的邏輯工作機關,事務中包括的諸操作要麼全做【全部成功】,要麼全不做【全部失敗】。

(2) 一緻性(Consistency):事務執行的結果必須是使資料庫從一個一緻性狀态變到另一個一緻性狀态,一緻性與原子性是密切相關的。

(3)隔離性(Isolation):使用者并不會受同一時間段内其他使用者操作的影響;看上去各個事務像是一前一後串行(Serially)發生的,哪怕目前有很多個并發事務。

(4)持續性/永久性(Durability):一個事務一旦送出,它對資料庫中資料的改變就應該是永久性的,其他操作或故障不應該對其有任何影響。

對事務解讀的其它視角(看看會不會被擊中,能不能建立連接配接): (老手可跳過)

事務就是一件完整要做的事情。是資料庫中各種資料項的一個執行單元。【原子性】

事務必須始終保持系統處于一緻的狀态,不管在任何給定的時間或并發的事務有多少。【一緻性】

事務是恢複和并發控制的基本機關。【原子性】

事務在關系資料庫中,一個事務可以是一條SQL語句,一組SQL語句或整個程式。這些操作要麼都做,要麼都不做,是一個不可分割的工作機關,即一個事務是一個原子操作。【原子性】

二、事務隔離級别

1. 隔離級别的由來及這個概念解決的問題

SQL的标準定義了4個隔離級别,設定一定的規則,限定事務内部的那些改變是可見的,那些是不可見的。低級别的事務一般會支援更高的并發處理,涉及更低的系統開銷。

插播兩個名詞,預熱一下。(老手可跳過)

兩個概念:

  • 送出【COMMIT】:代表 操作全部完成。下一步,事務就正常結束了。

    補充描述:【完成了事務的所有操作,具體地說就是将事務中所有對資料庫的更新寫回到磁盤上的實體資料庫中去,事務正常結束】。

  • 復原【ROLLBACK】:代表 操作撤消。下一步,需要将執行期間的修改回退成操作前的樣子。

    補充描述:【在事務運作的過程中發生了某種故障,事務不能繼續進行,系統将事務中對資料庫的所有以完成的操作全部撤消,滾回到事務開始的狀态或設定的復原點】。

2. 并發帶來的問題(按限制遞增排序)

(1)丢失更新【第一類】(Lost to modify):

現象:兩個事務都同時更新一行資料,但是第二個事務卻中途失敗退出, 導緻對資料的兩個修改都失效了。

例如:

  張三的工資為5000,事務A中擷取工資為5000,事務B擷取工資為5000,彙入100,并送出資料庫,工資變為5100,

  随後

  事務A發生異常,復原了,恢複張三的工資為5000,這樣就導緻事務B的更新丢失了。【收到的100元錢沒有了(人還在,錢沒了)】。

第一類丢失更新是需要一定條件的。即,兩個事務并發,一個復原、一個送出成功,并不一定會導緻丢失更新。

如果復原邏輯嚴謹,執行相關邏輯的逆運算前再重新取下最新的值,那麼資料的更新就不會丢失。

Tips:

目前主流的資料庫(MySQL和Oracle)已經消滅了第一類丢失更新

(2)髒讀(Dirty read):一個事務讀取到了另外一個事務沒有送出的資料。

髒讀就是指當一個事務正在通路資料,并且對資料進行了修改,而這種修改還沒有送出到資料庫中,這時,另外一個事務也通路這個資料,然後使用了這個未送出的資料。

例如:

  張三的工資為5000,事務A中把他的工資改為8000,但事務A尚未送出。

  與此同時,

  事務B正在讀取張三的工資,讀取到張三的工資為8000。

  随後,

  事務A發生異常,而復原了事務。張三的工資又復原為5000。

  最後,

  事務B讀取到的張三工資為8000的資料即為髒資料,事務B做了一次髒讀。

(3)不可重複讀(Unrepeatable read):在同一事務中,多次(大于1次)讀取同一資料,得到的内容不同。

補充描述:【并發update場景。在一個事務内,會多次讀同一資料,但在這個事務還沒有結束時,另外一個事務也通路該同一資料。那麼,在第一個事務中的兩次讀資料之間,由于第二個事務的修改,那麼第一個事務兩次讀到的的資料可能是不一樣的】。

這樣就發生了在一個事務内兩次讀到的資料是不一樣的,是以稱為是不可重複讀。

例如:

  在事務A中,讀取到張三的工資為5000,操作沒有完成,事務還沒送出。

  與此同時,

  事務B把張三的工資改為8000,并送出了事務。

  随後,

  在事務A中,再次讀取張三的工資,此時工資變為8000。在一個事務中前後兩次讀取的結果并不緻,導緻了不可重複讀。

  

不可重複讀的特例:第二類丢失更新。

有兩個并發事務同時讀取同一行資料,然後其中一個對它進行修改送出,而另一個也進行了修改送出。這就會造成第一次寫操作失效。

例如:

在事務A中,讀取到張三的存款為5000,操作沒有完成,事務還沒送出。

  與此同時,

  事務B,存儲1000,把張三的存款改為6000,并送出了事務。

  随後,

  在事務A中,存儲500,把張三的存款改為5500,并送出了事務,這樣事務A的更新覆寫了事務B的更新。

  

(4) 幻讀(Phantom/ˈfæntəm; fanˈtəm/ read):事務期間,多次(大于1次)查詢到的記錄數是不一樣的。

補充描述:【并發insert或delete場景。例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣】。

例如:

  目前工資為5000的員工有10人,事務A讀取所有工資為5000的人數為10人。

  此時,

  事務B插入一條工資也為5000的記錄。

  這是,事務A再次讀取工資為5000的員工,記錄為11人。多了一條記錄。這種情況稱為幻讀。

Tips:

不可重複讀的重點是修改,同樣的條件,你讀取過的資料,再次讀取出來發現值不一樣了

幻讀的重點在于新增或者删除,同樣的條件,第 1 次和第 2 次讀出來的記錄數不一樣

3、事務隔離級别,解決什麼并發問題,以及存在什麼并發問題

(1)READ_UNCOMMITTED

  這是事務最低的隔離級别,它充許另外一個事務可以看到這個事務未送出的資料。

  解決第一類丢失更新的問題,但是會出現髒讀、不可重複讀、第二類丢失更新的問題,幻讀 。

(2)READ_COMMITTED

  保證一個事務修改的資料送出後才能被另外一個事務讀取,即另外一個事務不能讀取該事務未送出的資料。

  解決第一類丢失更新和髒讀的問題,但會出現不可重複讀、第二類丢失更新的問題,幻讀問題

(3)REPEATABLE_READ

  保證一個事務相同條件下前後兩次擷取的資料是一緻的。

解決第一類丢失更新,髒讀、不可重複讀、第二類丢失更新的問題,但會出幻讀。

(4)SERIALIZABLE

  終結并發,全部串行。

  解決所有問題

一篇就夠了系列:事務隔離級小結

Tips:

(1)MySQL資料庫中,支援上面四種隔離級别,預設的為Repeatable read (可重複讀);

(2)Oracle資料庫中,隻支援Serializable (串行化)級别和Read committed (讀已送出)這兩種級别,其中預設的為Read committed級别。

(3)PostgreSQL不支援Read Uncommitted 隔離級别,不允許髒讀(Dirty read)

PostgreSQL實作的Repeatable read,也不會産生幻讀(Phantom read)

PostgreSQL預設的事務隔離級别:Read committed。