日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

通過這一篇文章讓你徹底了解事務,文章當中提供了很多真實示例,供大家參考學習!

目錄

一、什么是事務?

事務 是一組操作的集合,它是一個不可分割的工作單位,事務會把所有的操作 作為一個整體一起向系統提交 或 撤銷操作請求,即這些操作要么同時成功,要么同時失敗。

在關系數據庫中,一個事務可以是一條SQL語句,或者一組SQL語句;

在JAVA當中,一個事務可以是一個接口,也可以是service層當中的一個方法;

舉例:張三給李四轉賬1000塊錢,張三銀行賬戶的錢減少1000,而李四銀行賬戶的錢要增加1000。 這一組操作就必須在一個事務的范圍內,要么都成功,要么都失敗。

 

正常情況:轉賬這個操作, 需要分為以下這么三步來完成

 

異常情況:轉賬這個操作, 也是分為以下這么三步來完成 , 在執行第三步是報錯了, 這樣就導致張三減少1000塊錢, 而李四的金額沒變, 這樣就造成了數據的不一致, 就出現問題了。

 

為了解決上述的問題,就需要通過數據的事務來完成,我們只需要 在業務邏輯執行之前開啟事務,執行完畢后提交事務。如果執行過程中報錯,則回滾事務,把數據恢復到事務開始之前的狀態 。

 

注意: 默認MySQL的事務是自動提交的,也就是說,當執行完一條DML語句時,MySQL會立即隱式的提交事務。

二、事務操作

光說不練肯定不行,下面我們通過sql來演示以上場景,首先演示沒有事務的情況,然后再演示通過事務來控制的情況。事務控制mysql當中有兩種方式,我們分別進行演示。

數據準備:

DROP TABLE IF EXISTS account;

CREATE TABLE account ( 
	id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID', 
	NAME VARCHAR ( 10 ) COMMENT '姓名', 
	money DOUBLE ( 10, 2 ) COMMENT '余額' 
) COMMENT '賬戶表';

INSERT INTO account ( NAME, money ) VALUES ( '張三', 2000 ),( '李四', 2000 );

1、沒有事務會出現什么場景?

(1) 測試正常情況

-- 1. 查詢張三余額 
select * from account where name = '張三'; 
-- 2. 張三的余額減少1000 
update account set money = money - 1000 where name = '張三'; 
-- 3. 李四的余額增加1000 
update account set money = money + 1000 where name = '李四';

 

(2) 測試異常情況

UPDATE account set money = 2000;
-- 1. 查詢張三余額 
select * from account where name = '張三'; 
-- 2. 張三的余額減少1000 
update account set money = money - 1000 where name = '張三'; 
出錯了.... (這里是故意整的不加注釋,來模仿執行當前事務執行到中間報錯了)
-- 3. 李四的余額增加1000 
update account set money = money + 1000 where name = '李四';

我們把數據都恢復到2000, 然后再次一次性執行上述的SQL語句(出錯了… 這句話不符合SQL語 法,執行就會報錯),檢查最終的數據情況, 發現數據在操作前后不一致了。

 

2、控制事務方式一(手動提交)

mysql當中的事務默認是自動提交,什么是自動提交?

所謂自動提交就是每執行一條sql就進行提交一次。而實際我們在開發當中,往往會出現上面類似業務,多條sql同時執行,還要保證要么都成功要么都失敗。而在java當中我們并沒有看到過什么commit提交事務,什么rollback回滾事務的相關sql,這是因為我們使用的持久層框架都進行了封裝,例如MyBatis默認就是手動提交事務。而他的commit和rollback全權由框架來管控。

什么是手動提交事務?

只要不執行commit,那么mysql會認為你所執行的sql都是在一個事務當中,當commit的時候,假如成功了就都成功了,一旦執行的sql有異常,就全部回滾!

SELECT @@autocommit ;
SET @@autocommit = 0 ;
COMMIT;
ROLLBACK;

注意:

  • 上述的這種方式,我們是修改了事務的自動提交行為, 把默認的自動提交修改為了手動提交, 此時我們執行的DML語句都不會提交,需要手動的執行commit進行提交。
  • SET @@autocommit = 0 ; 設置手動提交 是臨時生效(只對當前會話有效),一旦會話關閉就沒了。想要設置永久手動提交可以通過mysql的配置文件當中添加 autocommit=0 然后重啟即可。

什么是會話?

會話并不是指的客戶端連接,一個客戶端連接可以創建多個會話,navicate應該都用過,通過下圖應該會一目了然!下面兩個查詢界面就是兩個會話!

 

sql測試:

提交事務之后會發現數據根本沒修改成功,原因就是執行過程遇到了報錯,然后自動會進行回滾!

-- 先改為手動提交
SET @@autocommit = 0 ;

-- 1. 查詢張三余額 
select * from account where name = '張三'; 
-- 2. 張三的余額減少1000 
update account set money = money - 1000 where name = '張三'; 
出錯了.... (這里是故意整的不加注釋,來模仿執行當前事務執行到中間報錯了)
-- 3. 李四的余額增加1000 
update account set money = money + 1000 where name = '李四';
-- 4. 提交事務 
COMMIT;

3、控制事務方式二(通過命令開啟事務)

除了上面所說的改為手動提交,還有一種就是通過sql來指定我要創建一個事務,commit的時候就是事務結束當前事務的時候。

  • 開啟事務: START TRANSACTION 或 BEGIN ;
  • 提交事務: COMMIT;
  • 回滾事務: ROLLBACK;

sql測試:

-- 開啟事務 
start transaction 
-- 1. 查詢張三余額 
select * from account where name = '張三'; 

-- 2. 張三的余額減少1000 
update account set money = money - 1000 where name = '張三'; 

-- 3. 李四的余額增加1000 
update account set money = money + 1000 where name = '李四'; 

-- 如果正常執行完畢, 則提交事務 
commit; 

-- 如果執行過程中報錯, 則回滾事務(commit失敗的時候會自動執行回滾,不需要我們管) 
-- rollback;

三、事務四大特性

事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為 ACID特性 。

  • 原子性(atomicity) 。一個事務是一個不可分割的工作單位,事務中包括的操作 要么都做,要么都不做 。
  • 一致性(consistency) 。事務必須是使 數據庫從一個一致性狀態變到另一個一致性狀態 。一致性與原子性是密切相關的。
  • 隔離性(isolation) 。一個事務的執行不能被其他事務干擾。即一個事務內部的操作和使用的數據對并發的其他事務是隔離的, 并發執行的各個事務之間不能互相干擾 。
  • 持久性(durability) 。持久性也稱永久性(permanence),指一個事務一旦提交,它 對數據庫中數據的改變就應該是永久性的 。接下來的其他操作或故障不應該對其有任何影響。

假設我現在去ATM機轉賬,將我的建設銀行卡的1000塊錢轉到中國銀行的卡里去。程序可能是這樣執行的:

第一步:從建設銀行卡的余額里扣除1000;

第二步:然后再從中國銀行的卡的余額里增加1000。

假如在第一步執行完之后服務器宕機了,那么顯然第二步將無法完成,我的建設銀行卡被扣了1000,但中國銀行的卡卻沒增加,我將白白損失了1000塊錢。在數據庫中,這就是所謂的“不一致性狀態”。

那么如何實現“一致性”呢?

事實上,ACID中的AID都是為了實現C的。事務的最終目的就是為了實現“ 一致性 ”。如果轉賬的操作具有原子性,那么在中途出現錯誤的時候發生回滾,就不會出現不一致的情況,可見,“原子性”和“一致性”是緊密聯系在一起的!

四、事務的隔離性

這里再重點提一下ACID當中的 隔離性(isolation) 。

隔離性是針對數據資源的并發訪問,規定了 各個事務之間相互影響的程度 。

舉例:為了疫情防控,各個城市針對于每個人的出行情況,出了對應的隔離級別,例如低風險的回老家就是3天兩檢,中風險的就是隔離14天,特別嚴重的就是集中隔離21天,等等。。。

隔離級別就是為了防止出現問題,而定的規則!

而事務的隔離性也分為了4種類型的隔離級別,這些隔離級別專門用于應對 數據資源并發訪問 下不同問題的場景。(就好比上面的例子,每個隔離級別都是對應一個場景,三天兩檢就是對應的低風險)。

1、并發事務下會產生什么問題?

什么是并發事務?

并發事務就是多個客戶端同時開啟事務,或者是多個會話同時開啟事務!Java當中一個接口就是一個事務,也可以稱之為一次會話,接口當中包含了很多sql,當并發訪問接口的時候會出現什么問題,也被稱為并發事務問題。當然本篇主要以數據庫并發事務產生的問題為主!

隔離級別專門用于應對 數據資源并發訪問 下不同問題的場景,那么究竟有哪些問題呢?有三個:臟讀、不可重復讀、幻讀

  • 臟讀: 讀到了還沒有提交事務的數據,簡稱讀未提交

舉例:如果一個事務B對數據進行了更改,但是還沒有提交,而另一個事務A就可以讀到事務B尚未提交的更新結果。這樣,當事務B進行回滾時,那么事務A開始讀到的數據就是一筆臟數據。

 

  • 不可重復讀: 同一個事務在事務過程中,對同一個數據進行讀取操作,讀取到的結果不同。

舉例:事務 A 多次讀取同一數據,但事務 B 在事務A多次讀取的過程中,對數據作了更新并提交,導致事務A多次讀取同一數據時,結果 不一致。

 

  • 幻讀: 一個事務按照條件查詢數據時,沒有對應的數據行,但是在插入數據時,又發現這行數據已經存在,好像出現了 “幻影”。

 

不可重復讀側重表達 讀-讀,幻讀則是說 讀-寫,用寫來證實讀的是鬼影。

如何避免:

  • 要避免臟讀,需要控制在事務沒有提交更新前,其他事務無法看到此事務的更新結果。
  • 要避免不可重復讀,假如查詢單條數據,可以通過行鎖,范圍查詢可以進行表鎖。
  • 要避免幻讀,需要將整張表都鎖住了。

2、事務的隔離級別

上述所說的"臟讀",“不可重復讀”,"幻讀"這些問題,其實就是數據庫讀一致性問題,必須由數據庫提供的事務隔離機制來進行解決。

 

  • 讀未提交(Read Uncommitted): 最低的隔離級別。一個事務可以讀取另一個事務沒有提交的更新結果。它是性能最好,也可以說它是最野蠻的方式,因為它壓根兒就不加鎖,所以根本談不上什么隔離效果,可以理解為沒有隔離。
  • 讀已提交(Read Committed): 一個事務的更新操作只有在提交了之后,才會被另一個事務讀取到同一筆數據更新后的結果。
  • 可重復讀(Repeatable Read): 在整個事務中,對同一筆數據的讀取結果是相同的,不管其他事務是否同時在對這筆數據進行更新,也不管這筆更新是否提交。
  • 串行化(SERIALIZABLE): 串行化就相當于處理一個人請求的時候,別的人都等著。讀的時候加共享鎖,也就是其他事務可以并發讀,但是不能寫。寫的時候加排它鎖,其他事務不能并發寫也不能并發讀。

注意:

  • MySQL默認事務隔離級別為可重復讀(RR),oracle默認事務隔離級別為讀已提交(RC)。
  • MySQL存在幻讀,而oracle存在不可重復讀和幻讀的情況!
  • 數據庫的事務隔離越嚴格,并發副作用越小,但付出的代價越大;

命令:

show variables like 't%_isolation';
select @@session.transaction_isolation;
select @@global.transaction_isolation;

注意:早期版本的mysql中用的變量名稱是tx_isolation,5.7.20版本之后,用的是transaction_isolation。

事務隔離級別分為會話和全局,會話在上面有提到過,一個客戶端可以有多個會話。會話一旦關閉,設置的就失效了。可以通過以下命令針對兩種進行修改, SESSION就是會話級別的 , GLOBAL全局的

SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }

如下示例,修改會話事務隔離級別為 READ UNCOMMITTED:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

(1)首先演示第一種:讀未提交(Read Uncommitted)

首先讀未提交會出現臟讀,那么我們就進行通過讀未提交來演示臟讀!

我們就還是以這兩條數據進行演示,其次我們要進行演示并發場景,就需要兩個客戶端,那么我們直接通過cmd打開兩個窗口 來進行連接mysql演示并發場景。

打開cmd通過命令進行連接: mysql -hlocalhost -u賬號 -p密碼

 

我的客戶端查出來的數據是亂碼的,應該是數據庫編碼有問題,但是這不影響我們演示

以下示例就是典型的臟讀,也就是讀取到了他沒有提交的數據,沒有提交就意味著數據并沒有存到磁盤,他很有可能會進行回滾,一旦回滾,而別的客戶端讀到了他沒提交的數據,而且還依賴這些臟數據做了別的操作,那將后果不堪設想!

 

(2)第二種:讀已提交(Read Committed)

讀已提交可以避免臟讀,這我們就不再測試了,想測試的可以按照上面的測試方法進行測試,看看是否會有以上問題!

讀已提交存在不可重復讀和幻讀的問題,同時也是oracle默認的隔離級別,我們重點演示不可重復讀!

 

其實仔細想想不可重復讀其實并不是很嚴重,他無非是在一個事務當中多次讀取可能值不一樣,但是他讀出來的都是提交過后的,也就意味著永遠是最新的數據,有時候未必是一件壞事!!!

(3)第三種:可重復讀(Repeatable Read)

可重復讀可以避免臟讀和不可重復讀情況,但是沒辦法避免幻讀,所以本次重點演示幻讀!

在測試這個的時候我們需要將id自增給關掉,自增的情況下不會出現這種情況!

新增的時候發現報錯,原因就是id為主鍵唯一,但是不是新增,在新增的時候實際上數據庫已經存在了id為3的數據,導致新增失敗,而他在這個事務當中不管怎么去查,也查不到id為3的數據,這就是幻覺!!!

 

說白了不可重復讀就是在當前事務當中,不管別的客戶端操作沒操作過數據,他查到的都是他開啟事務之前的數據。所以他根本不可能存在臟讀,也不可能存在不可重復讀。

(4)第四種:串行化(Serilizable)

讀的時候加共享鎖,也就是其他事務可以并發讀,但是不能寫。寫的時候加排它鎖,其他事務不能并發寫也不能并發讀。

 

五、本章總結

本篇文章要求掌握的:

  1. mysql當中事務默認是自動提交,可以改為手動提交
  2. 在一次事務當中,假如出現異常,事務會自動進行回滾
  3. 通過以上示例,如果還不知道事務是干什么的,建議多讀幾次,事務知識點太重要了,特別是學習后端的兄弟們!
  4. 數據庫當中,并發事務會引發什么問題?幻讀、不可重復讀、臟讀
  5. 隔離級別就是為了解決 并發事務可能會引發的問題 而誕生的,其中分為了四種級別,我們可以根據自己系統的情況進行自由選擇,事務隔離越嚴格,并發副作用越小,但付出的代價越大。正常開發當中一般都會采用默認的。
    四種分別是:讀未提交、讀已提交、不可重復讀、串行化。
  6. 并發事務會引發什么問題,還有隔離級別 這兩個都是面試經常會問的!!!

原文鏈接:
https://blog.csdn.NET/weixin_43888891/article/details/126024380?utm_source=tuicool&utm_medium=referral

分享到:
標簽:Mysql
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定