JDBC:資料庫中的事務
計算機中的事務
JDBC中的事務(關係型資料庫中):
1.事務的開啟:start transaction,有些資料庫圖形化軟體是預設開啟了事務的,無需再手動開啟,這樣執行增,刪,改語句時,才會有執行結果,即能執行成功.(即只有在開啟事務的前提下,增刪改sql語句才能執行成功,資料才會產生變化)
2.資料庫中如果connection.commit(true),即為自動提交,那麼每一條sql語句都是一個事務,如果想對多條sql語句進行控制,即事務的控制,則需要將自動提交改為手動提交,true改為false,這樣才能手動控制一組sql語句,讓它們同時成功或者同時失敗.
概念
例如:在關係資料庫中,一個事務可以是一條SQL語句,一組SQL語句或整個程式。
特性
事務應該具有4個屬性:原子性、一致性、隔離性、永續性。這四個屬性通常稱為ACID特性。
原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。
一致性(consistency)。事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
永續性(durability)。永續性也稱永久性(permanence),指一個事務一旦提交,它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
事務型別
(1)手動事務
手動事務允許顯式處理若干過程,這些過程包括:開始事務、控制事務邊界內的每個連線和資源登記、確定事務結果(提交或中止)以及結束事務。儘管此模型提供了對事務的標準控制,但它缺少一些內置於自動事務模型的簡化操作。例如,在手動事務中資料儲存區之間沒有自動登記和協調。此外,與自動事務不同,手動事務中事務不在物件間流動。
(2)自動事務
.NET 頁、XML Web services方法或 .NET Framework 類一旦被標記為參與事務,它們將自動在事務範圍內執行。您可以通過在頁、XML Web services 方法或類中設定一個事務屬性值來控制物件的事務行為。特性值反過來確定例項化物件的事務性行為。因此,根據宣告特性值的不同,物件將自動參與現有事務或正在進行的事務,成為新事務的根或者根本不參與事務。宣告事務屬性的語法在 .NET Framework 類、.NET 頁和 XML Web services 方法中稍有不同。
事務隔離性的實現原理:
大家都知道事務的ACID四大特性,其中隔離性代表事務的修改結果在什麼時候能被其他事務看到。這篇文章來介紹下資料庫中是如何實現事務隔離的。
隔離級別介紹
當資料庫上有多個事務同時執行的時候,就可能出現髒讀(dirty read)、不可重複讀(non reapeatable read)、幻讀(phantom read)的問題,為了解決這些問題,就有了“隔離級別”的概念。標準的隔離級別有:讀未提交(read uncommitted)、讀已提交(read commited)、可重複讀(repeatable read)序列化。其中隔離級別越嚴格,安全性越高,但資料庫的併發效能也就越低,往往需要在兩者之間找一個平衡點。
隔離的實現
隔離的實現主要有讀寫鎖和MVCC(Multi-Version Concurrency Control)多版本併發處理方式。
1.讀寫鎖
最簡單直接的的事務隔離實現方式,每次讀操作需要獲取一個共享鎖,每次寫操作需要獲取一個寫鎖。共享鎖之間不會產生互斥,共享鎖和寫鎖之間、以及寫鎖與寫鎖之間會產生互斥。當產生鎖競爭時,需要等待其中一個操作釋放鎖後,另一個操作才能獲取到鎖。
2. MVCC
在讀寫鎖中,讀和寫的排斥作用大大降低了事務的併發效率,於是人們又提出了能不能讓讀寫之間也不衝突的方法,就是讀取資料時通過一種類似快照的方式將資料儲存下來,這樣讀鎖就和寫鎖不衝突了。不同的事務session會看到自己特定版本的資料,即使其他的事務更新了資料,但是對本事務仍然不可見,本事務看到的資料始終是第一次查詢到的資料。在資料庫中,這個快照的處理方式叫多版本併發控制(Multi-Version Concurrency Control)。這種方式真正實現了非阻塞讀,只有在寫操作時才需要加行級鎖,因此併發效率更高。
在各個資料庫系統的,MVCC的實現機制不盡相同,下面來詳細介紹一下InnoDB是如何實現MVCC的,主要討論可重複讀級別的實現。
首先,需要了解兩個概念:ReadView、undo log、可見性判斷演算法
ReadView
ReadView其實就是上面提到的快照,每個事務在啟動後第一次執行查詢時會建立一份快照,一個事務快照的建立過程可以概括為:
- 檢視當前所有的未提交併活躍的事務,儲存在陣列中
- 選取未提交併活躍的事務中最小的XID,記錄在快照的xmin中
- 選取未提交事務中最大的XID,記錄快照在xmax中
ReadView主要是用來做可見性判斷的,即通過ReadView可以知道:哪些事務的提交結果對當前事務可見,哪些事務的提交結果對當前事務不可見。關於如何判斷可見性,後面部分會對可見性判斷演算法做出介紹。不過,我們可以先思考一個問題,在可重複讀隔離級別中,哪些事務的提交結果對當前事務可見呢?
undo log
剛才介紹了ReadView的基礎概念,提到了ReadView是事務的快照,但是通過ReadView僅僅能知道哪些事務的提交結果對當前事務可見,可是還是不知道當前事務的資料快照在哪啊。undo log就是來解決這個問題的。
undo log就是我們通常說的回滾日誌,undo log存放的是資料的歷史記錄,也可以叫資料的快照。
當一個事務要提交修改時:
1.會用排他鎖鎖定該行
2.將該行修改前的值Copy到undo log segment(回滾段)。
3.修改當前行的值,將該行的回滾指標指向undo log中修改前的行。
下圖描述了資料行和回滾段的資料關係。
undo log
如上圖,回滾日誌使用連結串列組織起來的,連結串列的每個節點都是一個數據的版本。InnoDB在每行記錄後面添加了三個欄位:
DB_ROW_ID: 包含一個隨著新行插入而單調遞增的行ID, 當由innodb自動產生聚集索引時,聚集索引會包括這個行ID的值,否則這個行ID不會出現在任何索引中。
DB_TRX_ID: 最後一次對本行提交修改的事務ID。同時,在回滾段中的每條記錄,也包含著該條日誌對應的事務ID。
DB_ROLL_PTR:指向寫入回滾段(rollback segment)的 undo log record (撤銷日誌記錄記錄)。回滾段的資料結構是連結串列,如果需要找到指定版本的資料,需要通過DB_ROLL_PTR指標沿著連結串列遍歷回滾段。
可見性判斷演算法
在介紹ReadView的時候,我們提出了可重複讀隔離級別中事務的可見性問題。這個問題答案很簡單,在可重複讀隔離級別中,對於當前事務tx_cur來說,tx_cur開始查詢之前的已提交事務都對tx_cur都可見,在tx_cur開始查詢之前的未提交事務和tx_cur開始查詢之後的所有事務對tx_cur均不可見。下面用一張草圖解釋一下。
可重複讀可見性
如圖所示,tx1-tx6分別是按時間順序的6個數據庫事務,假設當前啟動的事務是tx_cur,其中tx1-tx2是tx_cur開始查詢之前的已提交事務,tx3-tx5是tx_cur開始查詢時正在進行的活躍事務,tx6是開始查詢之後的提交的事務。
在tx_cur啟動事務並開始第一次查詢時,會建立一個ReadView,ReadView中儲存的是當前正在活躍的所有未提交事務id。在ReadView之前的已提交事務對tx_cur可見,在ReadView中以及ReadView之後的事務對tx_cur均不可見。
上面是一個簡單的事務可見性判斷過程,那麼當前事務該如何找到正確版本的資料呢?這個需要結合undo log一起來說。
我們可以結合undo log那節的示意圖來看。
1.首先查詢行的DB_TRX_ID欄位,該欄位記錄的是當前行最後提交的事務ID,簡稱為tx_id。
2.通過ReadView判斷tx_id是否對tx_cur可見。若可見,即找到了正確版本的資料;若不可見,則通過DB_ROLL_PTR指標找到undo log的上一個版本記錄,重複過程1。
總結
- 隔離的實現主要有讀寫鎖和MVCC(Multi-Version Concurrency Control)多版本併發處理方式。MVCC方式由於其讀寫不衝突的方式,相當於讀寫鎖效率更高。
- undo log和ReadView通過可見性判斷演算法實現了基本的MVCC,從而實現了事務的隔離。