日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

JDBC和数据库事务详解

發(fā)布時(shí)間:2023/12/29 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDBC和数据库事务详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

現(xiàn)在還在寫 JDBC 事務(wù)的文章,我覺得我一定是相當(dāng)?shù)?Out 了,現(xiàn)在主流的 java 應(yīng)用,框架都是分布式的,各種分布式的事務(wù),或者容器事務(wù)才是需要學(xué)習(xí)的重點(diǎn),在這里談 JDBC 確實(shí)有點(diǎn)不合時(shí)宜,但任何的 java 開發(fā)人員,如果不能夠深入的理解數(shù)據(jù)庫(kù)的事務(wù),那在做數(shù)據(jù)處理的方面就一定是有所欠缺的,另外確實(shí)很少有文章能夠談到 JDBC 和數(shù)據(jù)庫(kù)事務(wù)的精髓,希望這里能夠讓你深度的了解到什么是 JDBC 的事務(wù)以及它和數(shù)據(jù)庫(kù)的關(guān)系。

事務(wù)

事務(wù)應(yīng)該說是數(shù)據(jù)庫(kù)最核心的能力之一,對(duì)于任何和數(shù)據(jù)打交道的開發(fā)人員而言,是非常重要的

事務(wù)的原子性

事務(wù)的最基本功能是原子性。比如張三給李四異地打錢5000元,假設(shè)同一銀行異地手續(xù)費(fèi)是5‰,那么數(shù)據(jù)庫(kù)要干三件事情

  • 張三的賬戶余額扣除5025(含5‰手續(xù)費(fèi),中國(guó)特色)
  • 李四的賬戶余額增加5000
  • 銀行自己的賬戶余額增加25

這三件事情要么全部成功,要么全部失敗,絕對(duì)不能一些成功,一些失敗。

本地事務(wù)

對(duì)上面提出的問題,可以用一下代碼簡(jiǎn)單示范

String sql = "update Account set Balance = Balance + ? where id=?"try (Connection con = dataSource.getConnection(); PreparedStatement pstmtForSource = con.preparedStatement(sql); PreparedStatement pstmtForTarget = con.preparedStatement(sql); PreparedStatement pstmtForBlank = con.preparedStatement(sql)) {con.setAutoCommit(false); //關(guān)閉自動(dòng)提交,手動(dòng)事務(wù)開始pstmtForSource.setInt(1, -5025);pstmtForSource.setLong(2, sourceAccountId);pstmtForSource.executeUpdate();pstmtForTarget.setInt(1, +5000);pstmtForTarget.setLong(2, targetAccountId);pstmtForTarget.executeUpdate();pstmtForBank.setInt(1, +25);pstmtForBank.setLong(2, 1L);銀行自己卡號(hào)為1pstmtForBank.executeUpdate();con.commit(); //提交事務(wù) } catch (SQLException | RuntimeException | Error ex) {con.rollback(); //回滾事務(wù)throw ex; //不要忽略,繼續(xù)拋出,讓ATM界面層報(bào)錯(cuò) }

數(shù)據(jù)庫(kù)連接使用 setAutoCommit(false) 來開始一個(gè)事務(wù),此所做的所有事情都是原子性事務(wù)的一部分,最后一件事情做完后,調(diào)用 con.commit 來提交事務(wù)。如果整個(gè)過程有任何異常發(fā)生,可以調(diào)用 con.rollback() 來撤銷已經(jīng)被執(zhí)行的那部分修改。

數(shù)據(jù)庫(kù)連接的自動(dòng)提交默認(rèn)為 true,自動(dòng)提交為 true 的意思就是每句 SQL 執(zhí)行完成后,數(shù)據(jù)庫(kù)都會(huì)自動(dòng)根據(jù)成功與否來提交或回滾。這是毫無意義的,事務(wù)的原子性只有對(duì)多個(gè)操作而言才有意義,要么全部成功要么全部失敗這句話本身就隱含整個(gè)過程還有多個(gè) SQL 操作的意思。所謂,默認(rèn)的自動(dòng)提交也可以理解成無事務(wù)的意思。

一旦 setAutoCommit(false);就表示數(shù)據(jù)庫(kù)開啟一個(gè)需要手動(dòng)提交或回滾的事務(wù),從這句話開始,一直往后,到最接近的 commit 或 rollback 調(diào)用的代碼之間,所執(zhí)行的任何 SQL 修改都作為一個(gè)不可分割的一個(gè)整體,那理論性點(diǎn)的話說,就是一個(gè)原子。原子中所有語句要么都成功,要么都失敗。

特殊地,如果因?yàn)榫W(wǎng)絡(luò)故障、客戶端崩潰或者數(shù)據(jù)庫(kù)本身崩潰而導(dǎo)致既沒有commit也沒有rollback。等數(shù)據(jù)庫(kù)察覺到這個(gè)異常情況后,都視為 rollback。
一旦 commit 或 rollback 之后,下一個(gè)的事務(wù)又自動(dòng)開始了。當(dāng)前事務(wù)的最終結(jié)果已經(jīng)成事實(shí)了,板上釘釘了。更后面的提交或回滾的調(diào)用只針對(duì)下一個(gè)事務(wù)。從這里,你也可以往下延伸,即同一個(gè) connection 上可以執(zhí)行多個(gè)事務(wù),在 connection close 之前,你有多少個(gè) commit 就代表你提交了多少個(gè)事務(wù)。

保存點(diǎn)

數(shù)據(jù)庫(kù)事務(wù)回滾默認(rèn)是整體回滾,即回滾到事務(wù)剛開始的地方,這樣做是為了保證原子性。但數(shù)據(jù)庫(kù)也提供一種故意破壞原子性的功能,叫做保存點(diǎn)(Save Point),保存點(diǎn)可以使用專用的 SQL 語句當(dāng)前事務(wù)添加注冊(cè)。事務(wù)開始后,添加保存點(diǎn)的 SQL 和操作數(shù)據(jù)的 SQL 可以任意混合地不斷執(zhí)行,但在當(dāng)前事務(wù)范圍內(nèi),各保存點(diǎn)的名稱必須唯一,這樣,多個(gè)保存點(diǎn)可以把很多個(gè)數(shù)據(jù)操作 SQL 的分成很多小段。最后可以使用指定一個(gè)保存點(diǎn)名稱的 rollback 操作,這樣,就可以回滾到添加那個(gè)保存點(diǎn)的 SQL 的位置,而不是默認(rèn)的全部回滾。

數(shù)據(jù)庫(kù)支持此功能,JDBC 也支持暴露數(shù)據(jù)庫(kù)的這個(gè)能力,所以大家還是有必要了解這個(gè)概念。但說實(shí)話,用得非常少,應(yīng)用場(chǎng)景不多。

扁平事務(wù)和嵌套事務(wù)

對(duì)于所有數(shù)據(jù)庫(kù)而言,針對(duì)一個(gè)連接,事務(wù)的扁平結(jié)構(gòu)是默認(rèn)結(jié)構(gòu),結(jié)束上一個(gè)事務(wù)隱含了下一個(gè)事務(wù)的開始。事務(wù)總是被開始、結(jié)束、開始、結(jié)束,同一時(shí)刻,一個(gè)連接頂多能開啟一個(gè)事務(wù)。這種事務(wù)模型為扁平事務(wù)。

而對(duì)少數(shù)數(shù)據(jù)庫(kù)而言,針對(duì)一個(gè)連接,事務(wù)總是被開始、開始、結(jié)束、結(jié)束,但可能需要該數(shù)據(jù)產(chǎn)品特有的特殊的 SQL 命令。這是開啟了一個(gè)父事務(wù)和子事務(wù),父事務(wù)和子事務(wù)各自遵循自己的原子性,雙方的提交回滾彼此不干擾。這就是嵌套事務(wù)。這個(gè)概念,有點(diǎn)類似 spring 里面的 Nested 事務(wù),但這里是數(shù)據(jù)庫(kù)層面的,而且是針對(duì)同一個(gè)連接,對(duì)于絕大多數(shù)僅僅支持扁平事務(wù)的數(shù)據(jù)庫(kù)而言,可以讓當(dāng)前線程創(chuàng)建兩個(gè)不同的數(shù)據(jù)庫(kù)連接,然后在兩個(gè)不同的連接上各開啟一個(gè)事務(wù),屬于不同連接的不同事務(wù)各自遵循自己的原子性,各自的提交回滾彼此不干擾。這是扁平事務(wù)數(shù)據(jù)庫(kù)模擬嵌套事務(wù)的一個(gè)經(jīng)典用法。也是事務(wù)傳播屬性里,require new 和 nested 的實(shí)現(xiàn)原理。

數(shù)據(jù)庫(kù)事務(wù)實(shí)現(xiàn)大致原理

以 Oracle 為例,Oracle 數(shù)據(jù)都存儲(chǔ)在表空間上,表空間里面有一個(gè)段,叫做 Undo 段,在一個(gè)事務(wù)中,所進(jìn)行的所有增刪改操作被實(shí)施之前,都先要按照嚴(yán)格的順序在 Undo 段保持每條記錄的舊數(shù)據(jù)(對(duì)于 INSERT 操作而言,舊數(shù)據(jù)為空),這樣這對(duì)數(shù)據(jù)修改之前,Undo 段就保證備份了所有被操作記錄的原數(shù)據(jù)。如果最終被提交,清空 Undo 段中的數(shù)據(jù),如果最 終rollback,則按照 Undo 中事先備份好的原數(shù)據(jù)進(jìn)行逆向操作,每完成一項(xiàng)逆向操作,就清除一部分 Undo 數(shù)據(jù),最后全部回滾后,Undo 段的數(shù)據(jù)也被清空了。

如果網(wǎng)絡(luò)掉線或客戶端崩潰,一定超時(shí)后,數(shù)據(jù)庫(kù)能發(fā)現(xiàn)超時(shí)的“死鏈接”,數(shù)據(jù)庫(kù)會(huì)清除死鏈接,并且解開死連接所持有的鎖,并且根據(jù)和死連接相關(guān)聯(lián)的 Undo 段數(shù)據(jù)開始逆向操作以撤銷修改。

如果數(shù)據(jù)庫(kù)本身崩潰、數(shù)據(jù)庫(kù)所在操作系統(tǒng)奔潰、服務(wù)器硬件故障或者服務(wù)器停電導(dǎo)致數(shù)據(jù)庫(kù)死掉。人工采取恢復(fù)措施(例如換主板、或想辦法恢復(fù)電力供給)后重啟數(shù)據(jù)庫(kù),剛重啟的數(shù)據(jù)庫(kù)會(huì)拒絕所有客戶的連接申請(qǐng),專心看儲(chǔ)存介質(zhì)上是否有 Undo 數(shù)據(jù),如果有,開始撤銷,每撤銷一點(diǎn)就清除一點(diǎn) Undo 數(shù)據(jù)。考慮更極端一點(diǎn),如果在撤銷了一部分后,數(shù)據(jù)庫(kù)又出問題,那么大不了再重啟一次再來,反正還沒有被用于逆操作的 Undo 數(shù)據(jù)還在,當(dāng)所有的 Undo 數(shù)據(jù)被全部清空后,意味著所有的未提交操作全部非法數(shù)據(jù)都被逆操作了。這是標(biāo)志著數(shù)據(jù)庫(kù)得以全部恢復(fù),自此,數(shù)據(jù)庫(kù)服務(wù)器才開始接受外界申請(qǐng)連接,進(jìn)入正常的服務(wù)狀態(tài)。
總之,只要存儲(chǔ)數(shù)據(jù)的存儲(chǔ)介質(zhì)本身沒有損壞,無論多極端的軟件或硬件故障,數(shù)據(jù)庫(kù)一定能回滾。而事實(shí)上,存儲(chǔ)介質(zhì)本身也很可能有硬件層面的有鏡像容錯(cuò)能力,這就如虎添翼,更完美了。

Undo段故障

如果啟動(dòng)一個(gè)過于龐大的事務(wù),事務(wù)開始之后到提交之前的修改行為過于海量,當(dāng)會(huì)導(dǎo)致 Oracle 表空間 Undo 段所允許儲(chǔ)存資源被耗盡,此時(shí)應(yīng)用程序會(huì)得到異常。出現(xiàn)這個(gè)問題后,要仔細(xì)分析問題,辨別是應(yīng)用程序?qū)懙锰?#xff08;比如可以用小一點(diǎn)的事務(wù)實(shí)現(xiàn)同樣的功能)還是數(shù)據(jù)庫(kù)配置太二。最終決定由開發(fā)人員改應(yīng)用程序還是由 DBA 改數(shù)據(jù)庫(kù)軟硬件設(shè)置。

事務(wù)隔離級(jí)別

上面所講的事務(wù)的原子性,是對(duì)多條修改 SQL 具備意義。對(duì)于讀操作,事務(wù)同樣具備重大意義,這就是事務(wù)隔離級(jí)別 SQL 標(biāo)準(zhǔn)定義了4類隔離級(jí)別,包括了一些具體規(guī)則,用來限定事務(wù)內(nèi)外的哪些改變是可見的,哪些是不可見的。低級(jí)別的隔離級(jí)一般支持更高的并發(fā)處理,并擁有更低的系統(tǒng)開銷。

Read Uncommitted(讀取未提交內(nèi)容)

特別提醒,Oracle 不支持此級(jí)別!在該隔離級(jí)別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。本隔離級(jí)別很少用于實(shí)際應(yīng)用,因?yàn)樗男阅芤膊槐绕渌?jí)別好多少,但讀取到的數(shù)據(jù)極其不靠譜。讀取未提交的數(shù)據(jù),可能前腳剛讀到別人修改但未提交的數(shù)據(jù),后腳數(shù)據(jù)就被別人回滾撤銷了,自己讀到了一份完全無效的數(shù)據(jù)還渾然不知,這種最無節(jié)操的問題稱之為臟讀(Dirty Read)。

Read Committed(讀取提交內(nèi)容)

這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別(但不是 MySQL 默認(rèn)的)。這個(gè)級(jí)別可以解決臟讀(Dirty Read)的問題,一個(gè)事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變,如果其它事務(wù)反復(fù)修改數(shù)據(jù),當(dāng)前事務(wù)多次讀取同一條數(shù)據(jù)每次會(huì)讀到不同的數(shù)據(jù),這種現(xiàn)象叫做不可重復(fù)讀(Nonrepeatable Read)。

Repeatable Read(可重讀)

特別提醒,Oracle 不支持此級(jí)別!這是 MySQL 的默認(rèn)事務(wù)隔離級(jí)別。這個(gè)級(jí)別可以解決不可重復(fù)讀的(Nonrepeatable Read)問題。它確保同一事務(wù)的多次同一條數(shù)據(jù)的時(shí)候,每次會(huì)看到同樣的數(shù)據(jù)行。 但是其它事務(wù)任然還是可以添加和刪除同一張表的其它數(shù)據(jù),導(dǎo)致當(dāng)前事務(wù)反復(fù)看這張表的記錄總條數(shù),有時(shí)變多有時(shí)變少,就如同看街上閃爍的霓虹燈一樣,這種問題叫做幻讀(Phantom Read)

Serializable(串行化讀)

這是最高的隔離級(jí)別,連幻讀(Phantom Read)問題也被解決了。所有企圖操作同一張表(無論讀寫)的事務(wù)必須割舍掉所有并發(fā)性,串行化地排隊(duì)。對(duì)一張表而言,此級(jí)別完全不具備任何并發(fā)性,讀取到的數(shù)據(jù)絕對(duì)可靠。

隔離級(jí)別表格總結(jié)


越靠上,讀取到的數(shù)據(jù)越不嚴(yán)密,但并發(fā)度越高。越靠下,讀取到的數(shù)據(jù)越嚴(yán)密,但并發(fā)度越低下。典型的魚和熊掌難以兼得的問題,就連數(shù)據(jù)庫(kù)制造商自己都覺得難以取舍,就給了這個(gè)4檔變速箱,開發(fā)人員根據(jù)實(shí)際路況(項(xiàng)目具體情況)自己選。

隔離級(jí)別基本原理

由于部分?jǐn)?shù)據(jù)庫(kù)對(duì)4種級(jí)別支持得未必全,比如 Oracle 就僅僅支持兩個(gè)級(jí)別,而且每種數(shù)據(jù)庫(kù)的實(shí)現(xiàn)細(xì)節(jié)會(huì)稍微有所差異,所以我們講解一種理論上最簡(jiǎn)實(shí)現(xiàn)原理。實(shí)際數(shù)據(jù)庫(kù)實(shí)現(xiàn)完整隔離級(jí)別的原理只能比這個(gè)模型更復(fù)雜,不能更簡(jiǎn)單。

行鎖

假設(shè)每個(gè)數(shù)據(jù)行支持兩種鎖 RS 和 RX;RS 表示 Row Share,行共享鎖,不同的連接可以對(duì)同一行記錄同時(shí)上 RS 鎖,即行共享鎖,多個(gè)連接被允許同時(shí)對(duì)一條記錄上共享鎖;RX 表示 Row Exclusive,即行排它鎖,只能有一個(gè)連接可以對(duì)一行記錄上 RX 鎖。另外,鎖可以升級(jí),如果期望給一行數(shù)據(jù)上 RX 鎖而當(dāng)前行已經(jīng)存在一個(gè) RS 鎖,那么RS所會(huì)升級(jí)成RX鎖。但是反過來,鎖不能降低級(jí),如果已經(jīng)存在 RX鎖,希望上一個(gè) RS 鎖,那么必須等待解鎖。

已存在行鎖 期望新加行鎖 執(zhí)行方式
null RS 成功,加上RS鎖
null RX 成功,加上RX鎖
RS RS 成功,因?yàn)镽S是共享的,多個(gè)連接可同時(shí)鎖
RS RX 成功,因?yàn)镽S鎖支持升級(jí)為RX鎖
RX(其它連接加的) RS 等待解鎖再上鎖,因?yàn)镽X是排它的,可能超時(shí)
RX(同一連接加的) RS 忽略操作直接完成,鎖保持RX不變
RX(其它連接加的) RX 等待解鎖再上鎖,因?yàn)镽X是排它的,可能超時(shí)
RX(同一連接加的) RX 忽略操作直接完成,鎖保持RX不變

表鎖

類似地,對(duì)一張表級(jí)別的鎖而言, 也有兩種鎖 TS 和 TX,工作原理 RS, RX 非常類似,不再描述

修改語句和悲觀鎖查詢語句的行鎖和表鎖

對(duì)于修改語句,典型如下:

INSERT INTO MY_TABLE(C1, C2,, CN) VALUES(V1, V2,, VN); UPDATE MY_TABLE SET C1 = V1, C2 = V2, … CN = VN WHERE C1 = OV1; DELETE FROM MY_TABLE WHERE C1 = V1;

他們所對(duì)應(yīng)的鎖行為都是:

  • 首先對(duì)MYT_TABLE表上TS鎖
  • 再對(duì)被插入或修改的所有行上RX鎖(刪除后無行,行鎖操作忽略)。
  • 任何隔離級(jí)別下的悲觀鎖查詢

    SELECTFOR UPDATE

    均如此工作:

  • 首先在對(duì)被查詢的表上 TS 鎖
  • 再對(duì)查詢到的所有行上 RX 鎖。
  • 可以發(fā)現(xiàn),悲觀鎖查詢和類似修改語句

    普通查詢的行鎖和表鎖

    普通查詢語句在不同的隔離級(jí)別下工作機(jī)制不一樣

  • 如果當(dāng)前連接的隔離級(jí)別為未提交讀。不進(jìn)行任何行鎖和表鎖的操作,無論表或數(shù)據(jù)行是否有鎖以及是什么鎖,均不理會(huì),直接無條件取。
  • 如果當(dāng)前連接的隔離級(jí)別為提交讀。對(duì)所有被查到的行上RS鎖。如果其它連接已經(jīng)使用修改或悲觀鎖查詢讓相同行具備了RX鎖,因?yàn)镽X無法降級(jí)為RS,必須等待其它連接解鎖才能返回查詢結(jié)果,從而保證不會(huì)讀到未提交數(shù)據(jù)。
  • 如果當(dāng)前連接的隔離級(jí)別為可重復(fù)讀。先對(duì)表上TS鎖,然后對(duì)對(duì)所有被查到的行上RX鎖。從而保證其它連接不能對(duì)相同的數(shù)據(jù)行上RX鎖進(jìn)行修改或悲觀鎖查詢
  • 序列化讀隔離級(jí)別,直接對(duì)表上TX鎖,直接阻止其它連接對(duì)表上TS鎖,即其他連接不能對(duì)同一張表的任何數(shù)據(jù)行進(jìn)行修改或悲觀鎖查詢
  • 在一般的數(shù)據(jù)庫(kù)事務(wù)中,一個(gè)事務(wù)就代表著一個(gè)鏈接,事務(wù)的隔離級(jí)別既是鏈接的隔離級(jí)別,不同的鎖行為即代表了不同的執(zhí)行效率,這點(diǎn)是需要大家透徹理解的。

    總結(jié)

    以上是生活随笔為你收集整理的JDBC和数据库事务详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。