javascript
Spring JDBC-Spring事务管理之数据库事务基础知识
- 概述
- 數據庫事務的概念
- 原子性
- 一致性
- 隔離性
- 持久性
- 數據并發的問題
- 臟讀dirty read
- 不可重復讀unrepeatable read
- 幻象讀 phantom read
- 幻象讀和不可重復度的區別
- 第一類丟失更新
- 第二類丟失更新
- 數據庫鎖機制
- 事務隔離級別
- JDBC對事務的支持
概述
在使用Spring開發應用時,Spring的事務管理可能是被使用最多、應用最廣的功能。 Spring不但提供了和底層事務源無關的事務抽象,還提供了聲明性事務的功能,可以讓開發者從事務代碼中解放出來。
數據庫事務的概念
“一榮俱榮,一損俱損”。
數據庫事務必須同時滿足4個特性 (ACID):
原子性Atomic
一致性Consistency
隔離性Isolation
持久性Durability
原子性
表示組成一個事務的多個數據庫操作時一個不可分割的原子單元,只有所有的操作都成功,整個事務才提交。 事務中的任何一個數據庫操作失敗,已經執行的任何操作都必須回滾,讓數據庫返回到初始狀態。
一致性
事務操作成功后,數據庫所處的狀態和它的業務規則是一致的,即數據不會被破壞。 比如 A轉給B100元,不管操作成功與否,A賬戶和B賬戶的存款總額是不變的。
隔離性
在并發數據操作時,不同的事務擁有各自的數據空間,他們的操作不會對對方產生干擾, 準確的的說,并非要求做到完全無干擾,數據庫規定了多種事務隔離級別,不同的隔離級別對應不同的干擾程度。 隔離級別越高,數據一致性越好,但并發性越弱。
持久性
一旦事務提供成功后,事務中所有的數據操作都必須被持久化到數據庫中。 即使在提交事務后,數據庫馬上崩潰,再重啟數據時,也必須保證能通過某種機制恢復數據。
在這些事務特性中,數據的“一致性”是最終目標, 其他特性都是為了達到這個目標而采取的措施、要求或者手段。
數據庫管理系統一般采取redolog來保證原子行、一致性和持久性。
數據庫管理系統采用數據庫鎖機制保證事物的隔離性,當多個事務視圖對相同的數據機型操作時,只有持有鎖的事務才能操作數據,直到前面一個一個事務完成后,后面的事務才有機會對數據進行操作。 Oracle還使用了數據版本你的機制,在回滾段為數據的每一個變化保存一個版本,數據的更改不影響數據的讀取。
數據并發的問題
一個數據庫同時擁有多個訪問客戶端,這些客戶端都可用并發的方式訪問數據庫。 數據庫中相同的數據可能同時被多個事務訪問,如果沒有采取必要的隔離措施,就會導致各種并發問題,破壞數據的完整性。
這些問題可以歸結為5類,包括3類數據讀問題(臟讀、不可重復度、幻象讀)和2類數據更新問題(第一類丟失更新和第二類丟失更新)
臟讀dirty read
A 事務讀取了B事務尚未提交的更改數據,并在這個數據的基礎上進行操作。如果恰巧B事務回滾,那么A事務獨到的數據根本是不被承認的。
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 查詢賬戶為1000元 | |
| T4 | 取出500,更改余額為500 | |
| T5 | 查詢賬戶余額為500(臟讀) | |
| T6 | 撤銷事務,余額恢復為1000 | |
| T7 | 匯入100,余額更改為600 | |
| T8 | 提交事務 |
在這個場景中,B希望取款500,而后又撤銷了動作,而A相同的裝回轉入100,就因為A事務讀取了B事務尚未提交的數據,因而造成B賬戶白白丟失了500元。
在Oracle數據庫中,不會發生臟讀的情況。
不可重復讀unrepeatable read
不可重復讀是指A事務讀取了B事務已經提交的更改數據。
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 查詢賬戶為1000元 | |
| T4 | 查詢賬戶為1000元 | |
| T5 | 取出100,將余額更改為900 | |
| T6 | 提交事務 | |
| T7 | 查詢賬戶余額為900(和T4讀取的不一致) |
幻象讀 phantom read
A事務讀取了B事務提交的新增數據,這時A事務將出現幻象讀的問題。 幻象讀一般發生在計算統計數據的事務中。
舉個例子:假設銀行系統在同一個事務中兩次統計存款賬戶的總金額,再涼菜統計過程中,剛好新增了一個存款賬戶,并存入100元。 這時,兩次統計的金額將不一致。
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 統計總存款數為1000元 | |
| T4 | 新增一個存款賬戶,存入100 | |
| T5 | 提交事務 | |
| T6 | 再此統計存款總數為1100(幻象讀) |
如果新增數據剛好滿足事務的查詢條件,那么這個新數據就進入了事務的視野,因此產生了兩次統計結果不一致的情況。
幻象讀和不可重復度的區別
幻象讀是指讀到了其他已經提交的新增數據。
不可重復讀是指讀到了已經提交事務的更改數據(更改或者刪除)。
為了避免這兩種情況,采取的策略是不同的:
為了防止讀到更改數據,只需要對操作的數據添加行級鎖,組織操作中的數據發生變化。
而為了防止讀到新增數據,這往往需要添加表級鎖,將整張表鎖定,防止新增數據,(Oracle使用多版本數據的方式實現)
第一類丟失更新
A事務撤銷時,把已經提交的B事務的更新數據覆蓋了。
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 查詢賬戶為1000元 | |
| T4 | 查詢賬戶為1000元 | |
| T5 | 匯入100,將余額更改為1100 | |
| T6 | 提交事務 | |
| T7 | 取出100,將余額更改為900 | |
| T8 | 撤銷事務 | |
| T8 | 余額恢復為1000(丟失更新) |
A事務再撤銷時,“不小心”將B事務已經轉入賬戶的金額抹去了。
第二類丟失更新
A事務覆蓋B事務已經提交的數據,造成B事務所做操作丟失
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 查詢賬戶為1000元 | |
| T4 | 查詢賬戶為1000元 | |
| T5 | 取出100,將余額更改為900 | |
| T6 | 提交事務 | |
| T7 | 匯入100 | |
| T8 | 提交事務 | |
| T8 | 余額恢復為1100(丟失更新) |
由于支票轉賬事務覆蓋了取款事務對存款余額所做的更新,導致銀行損失了100, 相反,如果轉轉事務先提交,那么用戶將損失100元。
數據庫鎖機制
數據庫通過鎖機制解決并發訪問的問題。 不同的數據庫實現細節上存在差別,但是原理基本是一致的。
鎖的分類(oracle)
一、按操作劃分,可分為DML鎖、DDL鎖
二、按鎖的粒度劃分,可分為表級鎖、行級鎖、頁級鎖(mysql)
三、按鎖級別劃分,可分為共享鎖、排他鎖
四、按加鎖方式劃分,可分為自動鎖、顯示鎖
五、按使用方式劃分,可分為樂觀鎖、悲觀鎖
為了更改數據,數據庫必須在進行更改的行上施加行獨占鎖定,insert、update、delete、select for update語句都會隱士采用必要的行鎖定。
下面介紹oracle常用的5中鎖定
1、行共享鎖定:一般通過select for update 語句隱式獲得。行共享鎖定并不防止對數據行進行更改的操作,但是可以防止其他會話獲取獨占性數據表鎖定。允許進行多個并發的行共享和行獨占性鎖定,還允許進行數據表的共享或者采用共享行獨占鎖定。
2、行獨占鎖定:通過一條insert、update、delete語句隱士獲取,或者通過一條LOCK TABLE ROW EXCLUSIVE MODE語句顯示獲取。這個鎖可以防止其他會話獲取一個共享鎖定,共享行獨占鎖定或獨占鎖定。
3、表共享鎖定:通過LOCK TABLE IN SHARE MODE語句顯示獲得,這種鎖定可以防止其他會話獲取行獨占鎖定(insert,update,delete),或者防止其他表共享行獨占鎖定或表獨占鎖定,它允許在表中擁有多個行共享和表共享鎖定,該鎖定可以讓會話具有對表事務級一致性訪問,因為其他會話在用戶提交或者回溯該事務并釋放對該表的鎖定之前不能更改這個被鎖定的表。
4、表共享行獨占鎖定:通過 LOCK TABLE IN SHARE ROW EXCLUSIVE MODE語句顯示獲得。這種鎖定可以防止其他會話獲取一個表共享、行獨占或者表獨占鎖定,它允許其他行共享鎖定,它允許其他行共享鎖定。這種鎖定類似于表共享鎖定,只是一次只能對一個表放置一個表共享行獨占鎖定。如果A會話擁有該鎖定,則B會話可以執行select for update操作,但如果B會話試圖更新選擇的行,則需要等待。
5、表獨占:通過LOCK TABLE IN EXCLUSIVE MODE顯示獲得。這個鎖定防止其他會話對該表的任何鎖定。
事務隔離級別
因為直接使用數據的鎖比較麻煩,用戶可以設置事務的隔離級別來實現自動鎖機制。通過設置事務的隔離級別,數據庫就會分析事務中的SQL語句,然后自動為事務操作的數據資源加上適合的鎖。
| TRANSACTION_READ_UNCOMMITTED | 允許 | 允許 | 允許 | 不允許 | 允許 |
| TRANSACTION_READ_COMMITTED | 不允許 | 允許 | 允許 | 不允許 | 允許 |
| TRANSACTION_REPEATABLE_READ | 不允許 | 不允許 | 允許 | 不允許 | 不允許 |
| TRANSACTION_SERIALIZABLE | 不允許 | 不允許 | 不允許 | 不允許 | 不允許 |
JDBC對事務的支持
并不是所有的數據庫都支持事務,即使支持事務的數據庫也并非支持所有的事務隔離級別,用戶可以通過Connection的getMetaData()方法獲取DatabaseMetaData對象,并通過該對象的supportsTransactions()、supportsTransactionIsolationLevel(int level)方法查看底層數據庫的事務支持情況。
Connection默認情況下是自動提交的,也即每條執行的SQL都對應一個事務,為了能夠將多條SQL當成一個事務執行,必須先通過Connection的setAutoCommit(false)阻止Connection自動提交,并可通過Connection的setTransactionIsolation()設置事務的隔離級別,Connection中定義了對應SQL 92標準4個事務隔離級別的常量。通過Connection的commit()提交事務,通過Connection的rollback()回滾事務。
下面是典型的JDBC事務數據操作的代碼:
Connection conn ; try{ conn = DriverManager.getConnection();//①獲取數據連接 conn.setAutoCommit(false); //②關閉自動提交的機制 //③設置事務隔離級別 conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Statement stmt = conn.createStatement(); int rows = stmt.executeUpdate( "INSERT INTO t_topic ALUES(1,’tom’) " ); rows = stmt.executeUpdate( "UPDATE t_user set topic_nums = topic_nums +1 "+ "WHERE user_id = 1"); conn.commit();//④提交事務 }catch(Exception e){ … conn.rollback();//⑤回滾事務 }finally{ … }在JDBC2.0中,事務只有兩個操作: 提交或者回滾。
在JDBC3.0(Java1.4以及以后的版本)引入了保存點特性。 JDBC定義了SavePoint接口,提供在一個更細粒度的事務控制機制。當設置了一個保存點后,可以rollback到該保存點處的狀態,而不是rollback整個事務。Connection接口的setSavepoint和releaseSavepoint方法可以設置和釋放保存點
并非所有數據庫都支持保存點功能,用戶可以通過DatabaseMetaData的supportsSavepoints()方法查看是否支持。
總結
以上是生活随笔為你收集整理的Spring JDBC-Spring事务管理之数据库事务基础知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C3P0-数据库连接池解读
- 下一篇: Spring JDBC-Spring事务