1. 程式人生 > >資料庫學習-事務

資料庫學習-事務

1 事務

1.1 什麼是事務

事務可以理解為一個獨立的工作單元, 在這個獨立的工作單元中, 有一組操作,放在事務(獨立工作單元)中的多個操作, 要麼全部執行成功, 要麼全部執行失敗

事務指的是滿足 ACID 特性的一組操作,可以通過 Commit 提交一個事務,也可以使用 Rollback 進行回滾。

1.2 ACID(事務四大特性)

1.2.1 原子性(Atomicity)

事務被視為不可分割的最小單元,事務的所有操作要麼全部提交成功,要麼全部失敗回滾。

對於一個事務來說, 不能只成功執行其中的一部分操作, 這就是事務的原子性。

回滾可以用日誌來實現,日誌記錄著事務所執行的修改操作,在回滾時反向執行這些修改操作即可。

1.2.2 一致性(Consistency)

資料庫在事務執行前後保持一致性狀態。在一致性狀態下,所有事務對一個數據的讀取結果都是相同的。

比如一個轉賬的例子:A初始200 ,B初始300,A給B轉100。

轉賬前一致性狀態是:A(200元),B(300元)
轉賬100元成功後一致性狀態:A(100元),B(400元)
如果轉賬失敗,一致性狀態應該回滾到轉賬前的狀態:A(200元),B(300元)

1.2.3 隔離性(Isolation)

一個事務所做的修改在最終提交以前,對其它事務是不可見的。

隔離性是當多個使用者併發訪問資料庫時,比如同時操作同一張表時,資料庫為每一個使用者開啟的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。

事務有四種隔離級別(從低到高: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE)

即(未讀提交、讀提交、可重複讀、序列化)

1.2.4 永續性(Durability)

永續性是指一個事務一旦被提交了,那麼對資料庫中的資料的改變就是永久性的,即便是在資料庫系統遇到故障的情況下也不會丟失提交事務的操作。

可以通過資料庫備份和恢復來實現,在系統發生崩潰時,使用備份的資料庫進行資料恢復。

事務的 ACID 特性概念簡單,但不是很好理解,主要是因為這幾個特性不是一種平級關係:

  • 只有滿足一致性,事務的執行結果才是正確的。
  • 在無併發的情況下,事務序列執行,隔離性一定能夠滿足。此時只要能滿足原子性,就一定能滿足一致性。
  • 在併發的情況下,多個事務並行執行,事務不僅要滿足原子性,還需要滿足隔離性,才能滿足一致性。
  • 事務滿足持久化是為了能應對資料庫崩潰的情況。

img

MySQL 預設採用自動提交模式。也就是說,如果不顯式使用START TRANSACTION語句來開始一個事務,那麼每個查詢都會被當做一個事務自動提交。

1.3 併發中可能出現的問題

1.3.1 丟失修改(Lost update)

如果多個執行緒操作,基於同一個查詢結構對錶中的記錄進行修改,那麼後修改的記錄將會覆蓋前面修改的記錄,前面的修改就丟失掉了,這就叫做更新丟失。這是因為系統沒有執行任何的鎖操作,因此併發事務並沒有被隔離開來。

T1 和 T2 兩個事務都對一個數據進行修改,T1 先修改,T2 隨後修改,T2 的修改覆蓋了 T1 的修改。

img

1.3.2 讀髒資料(Dirty Reads)

A事務讀取B事務尚未提交的資料並在此基礎上操作,而B事務執行回滾,那麼A讀取到的資料就是髒資料。

T1 修改一個數據,T2 隨後讀取這個資料。如果 T1 撤銷了這次修改,那麼 T2 讀取的資料是髒資料。

img

1.3.3 不可重複讀(Non-repeatable Reads)

如果在一個事務中多次讀取同一個資料, 正好在兩次讀取之間, 另外一個事務確實已經完成了對該資料的修改並提交, 那問題就來了: 可能會出現多次讀取結果不一致的現象。

T2 讀取一個數據,T1 對該資料做了修改。如果 T2 再次讀取這個資料,此時讀取的結果和第一次讀取的結果不同。

img

1.3.4 幻影讀

  • 容易搞混不可重複讀幻讀 ,都是說兩次讀取資料不一致。
  • 不可重複讀主要是說多次讀取一條記錄, 發現該記錄中某些列值被修改過。
  • 幻讀主要是說多次讀取一個範圍內的記錄(包括直接查詢所有記錄結果或者做聚合統計), 發現結果不一致(標準檔案一般指記錄增多(一般來講), 記錄的減少應該也算是幻讀(猜測))。
  • 指兩次執行同一條 select 語句會出現不同的結果,第二次讀會增加一資料行,並沒有說這兩次執行是在同一個事務中。 通俗的講,一個執行緒中的事務讀取到了另外一個事務insert的資料。

T1 讀取某個範圍的資料,T2 在這個範圍內插入新的資料,T1 再次讀取這個範圍的資料,此時讀取的結果和和第一次讀取的結果不同。

img

1.4 封鎖粒度

MySQL 中提供了兩種封鎖粒度:行級鎖以及表級鎖

應該儘量只鎖定需要修改的那部分資料,而不是所有的資源。鎖定的資料量越少,發生鎖爭用的可能就越小,系統的併發程度就越高。

但是加鎖需要消耗資源,鎖的各種操作(包括獲取鎖、釋放鎖、以及檢查鎖狀態)都會增加系統開銷。因此封鎖粒度越小,系統開銷就越大。

總的來說:能少鎖就少鎖(減少系統開銷)

1.5 封鎖型別

1.5.1 讀寫鎖

  • 排它鎖(Exclusive),簡寫為X鎖,又稱為寫鎖。
  • 共享鎖(Shared),簡寫為S鎖,又稱為讀鎖。

有以下兩個規定:

  1. 一個事務對資料物件A加了X鎖,就可以對A讀取和更新。加鎖期間其它事務不能對A加任何鎖。
  2. 一個事務對資料物件A加了S鎖,可以對A進行讀取操作,當不能進行更新操作。加鎖期間其它事務能對A加S鎖當不能加X鎖。

鎖的相容關係如下(即只有S鎖可以加S鎖,其他情況都不可以):

- X S
X × ×
S ×

1.5.2 意向鎖

使用意向鎖(Intention Locks)可以更容易地支援多粒度封鎖。

意向鎖存在原因:在存在行級鎖和表級鎖的情況下,事務 T 想要對錶 A 加 X 鎖,就需要先檢測是否有其它事務對錶 A 或者表 A 中的任意一行加了鎖,那麼就需要對錶 A 的每一行都檢測一次,這是非常耗時的。

意向鎖在原來的 X/S 鎖之上引入了 IX/IS,IX/IS 都是表鎖,用來表示一個事務想要在表中的某個資料行上加 X 鎖或 S 鎖。有以下兩個規定:

  • 一個事務在獲得某個資料行物件的 S 鎖之前,必須先獲得表的 IS 鎖或者更強的鎖;
  • 一個事務在獲得某個資料行物件的 X 鎖之前,必須先獲得表的 IX 鎖。

意向鎖作用:通過引入意向鎖,事務 T 想要對錶 A 加 X 鎖,只需要先檢測是否有其它事務對錶 A 加了 X/IX/S/IS 鎖,如果加了就表示有其它事務正在使用這個表或者表中某一行的鎖,因此事務 T 加 X 鎖失敗。

各種鎖的相容關係如下:

- X IX S IS
X × × × ×
IX × ×
S × ×
IS ×

解釋如下:

  • 任意 IS/IX 鎖之間都是相容的,因為它們只是表示想要對錶加鎖,而不是真正加鎖;
  • S 鎖只與 S 鎖和 IS 鎖相容,也就是說事務 T 想要對資料行加 S 鎖,其它事務可以已經獲得對錶或者表中的行的 S 鎖。

1.6 隔離級別

事務的隔離級別有四種,由低到高依次為:

Read uncommited (未授權讀取、讀未提交) Read commited(授權讀取、讀提交) Repeatable read(可重複讀取) Serializable(序列化)

隔離級別高的資料庫的可靠性高,但併發量低,而隔離級別低的資料庫可靠性低,但併發量高,系統開銷小。

1.6.1 未提交讀(Read uncommited)

事務中的修改,即使沒有提交,對其它事務也是可見的。

如果一個事務已經開始寫資料,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行資料。 該隔離級別可以通過**排他鎖(X鎖)**實現。這樣就避免了更新丟失,卻可能出現髒讀。也就是說事務B讀取到了事務A未提交的資料

ep: 一個售票系統,A和B是售票員,他們分別是兩個不同視窗的員工,現在售票系統只剩下3張票,此時小明來A這裡買3張票,小張來B買票,A查到餘票還有就給接了訂單,就要執行第三步的時候,B接到小張的請求查詢有沒有餘票。B看到A賣出了3張票,於是拒絕賣票。但是A系統出了問題,第三步執行失敗,資料庫為保證原子性,資料進行了回滾,也就是說一張票都沒賣出去。

總結:一個事務還沒提交,而別的事務可以看到他其中修改的資料的後果,也就是髒讀。

1.6.2 提交讀(Read commited)

一個事務只能讀取已經提交的事務所做的修改。換句話說,一個事務所做的修改在提交之前對其它事務是不可見的。

讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。

該隔離級別避免了髒讀,但是卻可能出現不可重複讀。事務A事先讀取了資料,事務B緊接更新了資料,並提交了事務,而事務A再次讀取該資料時,資料已經發生了改變。

大多數資料庫系統的預設隔離級別是READ CIMMITTED。 ep:

還是A和B銷售員,餘票4張,小明來A請求3張訂票單,A受訂單,要賣出3張票,上面的銷售步驟執行中的時候,小張也來B那裡買票,由於A的銷售事務執行到一半,B事務沒有看到A的事務執行,讀到的票數是3,準備接受訂單的時候,A的銷售事務完成了,此時B的系統變成顯示0張票,此時只能拒絕訂單了。

總結:這就是A的事務執行到一半,而B看不到他執行的操作,所以看到的是舊資料,也就是不可重複讀。

1.6.3 可重複讀(Repeatable read)

保證在同一個事務中多次讀取同樣資料的結果是一樣的。

可重複讀是指在一個事務內,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該同一資料。 在第一個事務中的兩次讀資料之間,即使第二個事務對資料進行修改,第一個事務兩次讀到的的資料是一樣的。這樣就發生了在一個事務內兩次讀到的資料是一樣的,因此稱為是可重複讀。 讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。這樣避免了不可重複讀取和髒讀,但是有時可能出現幻象讀。(讀取資料的事務)這可以通過“共享讀鎖”和“排他寫鎖”實現。

ep:

銷售部門有規定,如果銷售記錄低於規定的值,要扣工資,此時經理在後端控制檯查看了一下小明的銷售記錄,發現銷售記錄達不到規定的次數,心裡暗喜,準備列印好銷售清單,理直氣壯和小明提出,沒想到打印出來的時候發現銷售清單裡面銷售數量增多了幾條,剛剛好達到要求,氣的經理撕了清單紙。原來是小明在就要列印的瞬間賣出了幾張票,因此避過了減工資的血光之災。

雖然讀取同一條資料可以保證一致性,但是卻不能保證沒有插入新的資料,就是幻讀。

1.6.4 序列化(Serializable)

強制事務序列執行。

提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能併發執行。

如果僅僅通過行級鎖是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也花費最高,效能很低,一般很少使用,在該級別下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀。

對於多數應用程式,可以優先考慮把資料庫系統的隔離級別設為Read Committed。它能夠避免髒讀,而且具有較好的併發效能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。

大多數資料庫的預設級別就是Read committed,比如Sql Server , Oracle。MySQL的預設隔離級別就是Repeatable read。

1.7 多版本併發控制系統(MVCC)

多版本併發控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 儲存引擎實現隔離級別的一種具體方式,用於實現提交讀和可重複讀這兩種隔離級別。而未提交讀隔離級別總是讀取最新的資料行,無需使用 MVCC。可序列化隔離級別需要對所有讀取的行都加鎖,單純使用 MVCC 無法實現。

MVCC用於實現未讀提交和可重複讀這兩個隔離級別,不能解決幻讀。

1.7.1 版本號

  • 系統版本號:是一個遞增的數字,每開始一個新的事務,系統版本號就會自動遞增。
  • 事務版本號:事務開始時的系統版本號。

1.7.2 隱藏的列

MVCC 在每行記錄後面都儲存著兩個隱藏的列,用來儲存兩個版本號:

  • 建立版本號:指示建立一個數據行的快照時的系統版本號;
  • 刪除版本號:如果該快照的刪除版本號大於當前事務版本號表示該快照有效,否則表示該快照已經被刪除了。

1.7.3 Undo 日誌

MVCC 使用到的快照儲存在 Undo 日誌中,該日誌通過回滾指標把一個數據行(Record)的所有快照連線起來。

img

1.8 Next-Key Locks

Next-Key Locks 是 MySQL 的 InnoDB 儲存引擎的一種鎖實現。

MVCC 不能解決幻讀的問題,Next-Key Locks 就是為了解決這個問題而存在的。在可重複讀(REPEATABLE READ)隔離級別下,使用 MVCC + Next-Key Locks 可以解決幻讀問題

1.8.1 Record Locks

鎖定一個記錄上的索引,而不是記錄本身。

如果表沒有設定索引,InnoDB 會自動在主鍵上建立隱藏的聚簇索引,因此 Record Locks 依然可以使用。

###1.8.2 Gap Locks

鎖定索引之間的間隙,但是不包含索引本身。例如當一個事務執行以下語句,其它事務就不能在 t.c 中插入 15。

SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;

1.8.3 Next-Key Locks

它是 Record Locks 和 Gap Locks 的結合,不僅鎖定一個記錄上的索引,也鎖定索引之間的間隙。例如一個索引包含以下值:10, 11, 13, and 20,那麼就需要鎖定以下區間:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

2 用自己的話總結事務

事務就是指滿足ACID的特性的一組操作,要麼全部做,要麼全部不做。

說到事務那麼就要說說事務的四大特性即:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和永續性(Durability)。

  • 原子性:事務要麼全部做要麼全部不做
  • 一致性:事務在執行前後都保持一致性狀態
  • 隔離性:事務在修改之前對其他事務是不可見的
  • 永續性:事務一旦提交那麼修改就會永久儲存到資料庫中。

封鎖型別主要分為讀寫鎖和意向鎖。

  • 讀寫鎖
    • 讀鎖(S鎖):加了讀鎖只可以進行讀操作,讀鎖上可以繼續加讀鎖
    • 寫鎖(X鎖):加了寫鎖可以進行讀取、修改操作,寫鎖上不能新增其他的鎖(包括寫鎖)
  • 意向鎖:使用意向鎖(Intention Locks)可以更容易地支援多粒度封鎖。 意向鎖都是表鎖,意向鎖就像一個房管,不管使用者是要使用整間房(整個表),還是使用房間的一部分(某行),其他使用者等級要使用這個房間是都會先去房管那查詢,這個房價是否被佔用(是否加了意向鎖),如果房間被佔用了,房管就不會把這間房立即分給房客,而是等到原來的房客用完房間為止。

說到事務那麼另外一個東西也要說說,那就是事務的隔離級別,在資料庫中總共有四種隔離級別從低到高為:未提交度、提交度、可重複讀、序列化。

  • 未提交讀:事務在未提交的時候對其他事務也是可見的(其他事務也是可以訪問這個資料的)。
  • 提交讀:一個事務的修改在未提交前對其他事務是不可見的。
  • 可重複讀:保證在同一個事務中多次讀取的結果是一樣的。
  • 序列化:事務只能一個接著一個地執行,但不能併發執行。

在Mysql中的隔離級別預設是使用的可重複讀

多版本併發控制( MVCC)是 MySQL 的 InnoDB 儲存引擎實現隔離級別的一種具體方式,應用於實現提交讀和可重複讀這兩種隔離級別,但是MVCC不能解決幻讀的問題,MVCC+Next-Key Locks可以解決幻讀問題。