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