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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第六节:深究事务的相关性质、隔离级别及对应的问题、死锁相关

發(fā)布時間:2023/12/13 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第六节:深究事务的相关性质、隔离级别及对应的问题、死锁相关 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一. 相關(guān)概念?

?  前面系列中的章節(jié)的:?第二十二節(jié): 以SQLServer為例介紹數(shù)據(jù)庫自有的鎖機制(共享鎖、更新鎖、排它鎖等)和事務(wù)隔離級別? 介紹了各種鎖以及事務(wù)的隔離級別,是從數(shù)據(jù)庫的角度進行介紹的,本章節(jié)是通過EF Core為載體,介紹事務(wù)隔離級別和相關(guān)問題,與上述章節(jié)有些許重復(fù)的內(nèi)容。

1. 什么是事務(wù)

  事務(wù)(Transaction)是由一系列對系統(tǒng)中數(shù)據(jù)進行訪問與更新的操作所組成的一個程序執(zhí)行邏輯單元。

2. 事務(wù)的特征

  事務(wù)具有 4 個基本特征,分別是:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Duration),簡稱:ACID。

  (1).原子性:指事務(wù)必須是一個原子的操作序列單元。事務(wù)中包含的各項操作在一次執(zhí)行過程中,只允許出現(xiàn)兩種狀態(tài)之一。 ? 全部執(zhí)行成功 ? 全部執(zhí)行失敗,任何一項操作都會導(dǎo)致整個事務(wù)的失敗,同時其它已經(jīng)被執(zhí)行的操作都將被撤銷并回滾,只有所有的操作全部成功,整個事務(wù)才算是成功完成.

  (2).一致性:事務(wù)的一致性是指事務(wù)的執(zhí)行不能破壞數(shù)據(jù)庫數(shù)據(jù)的完整性和一致性,一個事務(wù)在執(zhí)行之前和執(zhí)行之后,數(shù)據(jù)庫都必須處以一致性狀態(tài)。比如:如果從 A 賬戶轉(zhuǎn)賬到 B 賬戶,不可能因為 A 賬戶扣了錢,而 B 賬戶沒有加錢,無論 A 和 B 怎么轉(zhuǎn)賬,系統(tǒng)中總額是固定的,不可能因為 A 和 B 轉(zhuǎn)賬導(dǎo)致系統(tǒng)總額缺斤少兩。

  (3).隔離性:指在并發(fā)環(huán)境中,并發(fā)的事務(wù)是互相隔離的,一個事務(wù)的執(zhí)行不能被其它事務(wù)干擾。也就是說,不同的事務(wù)并發(fā)操作相同的數(shù)據(jù)時,每個事務(wù)都有各自完整的數(shù)據(jù)空間。一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對其它并發(fā)事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)是不能互相干擾的。(詳見下面隔離級別)

  (4).持久性:事務(wù)的持久性是指事務(wù)一旦提交后,數(shù)據(jù)庫中的數(shù)據(jù)必須被永久的保存下來。即使服務(wù)器系統(tǒng)崩潰或服務(wù)器宕機等故障。只要數(shù)據(jù)庫重新啟動,那么一定能夠?qū)⑵浠謴?fù)到事務(wù)成功結(jié)束后的狀態(tài)。

二. 事務(wù)隔離級別以及引發(fā)的問題

1. 隔離級別

 (1).讀未提交(READ_UNCOMMITTED):讀未提交,該隔離級別允許臟讀取,其隔離級別是最低的。換句話說,如果一個事務(wù)正在處理某一數(shù)據(jù),并對其進行了更新,但同時尚未完成事務(wù),?因此還沒有提交事務(wù);而以此同時,允許另一個事務(wù)也能夠訪問該數(shù)據(jù)。

  【引發(fā)的問題:臟讀】

 (2).讀已提交(READ_COMMITTED) :事務(wù)執(zhí)行的時候只能獲取到其它事務(wù)已經(jīng)提交的數(shù)據(jù),獲取不到未提交的數(shù)據(jù)。

  【解決了“臟讀”,但是解決不了“不可重復(fù)讀”】

 (3).可重復(fù)讀(REPEATABLE_READ):保證在事務(wù)處理過程中,多次讀取同一個數(shù)據(jù)時,該數(shù)據(jù)的值和事務(wù)開始時刻是一致的。

  【解決了“臟讀”和“不可重復(fù)度”,但是解決不了“幻讀”】

 (4).順序讀(SERIALIZABLE):最嚴格的事務(wù)隔離級別。它要求所有的事務(wù)排隊順序執(zhí)行,即事務(wù)只能一個接一個地處理,不能并發(fā)。

  【解決上述所有情況】

注:4 種事務(wù)隔離級別從上往下,級別越高,并發(fā)性越差,安全性就越來越高。一般數(shù)據(jù)默認級別是讀已提交或可重復(fù)讀。

PS:常見數(shù)據(jù)庫的默認級別:

 ①:MySQL 數(shù)據(jù)庫的默認隔離級別是 Repeatable read 級別。

 ②:Oracle數(shù)據(jù)庫中,只支持 Seralizable 和 Read committed級別,默認的是 Read committed 級別。

 ③:SQL Server 數(shù)據(jù)庫中,默認的是 Read committed(讀已提交) 級別。

2.引發(fā)的問題

 (1).臟讀(Dirty Read):第一個事務(wù)讀取第二個事務(wù)正在更新的數(shù)據(jù),如果第二個事務(wù)還沒有更新完成,那么第一個事務(wù)讀取的數(shù)據(jù)將是一半為更新過的,一半還沒更新過的數(shù)據(jù)。

 (2).不可重復(fù)讀(Unrepeatable Read):如果一個用戶在一個事務(wù)中多次讀取一條數(shù)據(jù),而另外一個用戶則同時更新啦這條數(shù)據(jù),造成第一個用戶多次讀取數(shù)據(jù)不一致。

 (3).幻讀(Phantom Read):指同樣的事務(wù)操作,在前后兩個時間段內(nèi)執(zhí)行對同一個數(shù)據(jù)項的讀取,可能出現(xiàn)不一致的結(jié)果集。

3. 案例測試

(前提:初始值userAge均為1000的且id為01 和 02 兩條數(shù)據(jù))

 (1).臟讀測試:事務(wù)1兩條數(shù)據(jù)分別-500,正常事務(wù)提交后,這兩條數(shù)據(jù)的userAge的值應(yīng)該均為500;將事務(wù)2設(shè)置成讀未提交(IsolationLevel.ReadUncommitted 即允許臟讀),?查出來的結(jié)果是:500,1000,即臟讀數(shù)據(jù)。

代碼分享

?

1 { 2 //1.事先準備刪除所有數(shù)據(jù),插入兩條指定數(shù)據(jù) 3 using (EFDB01Context db = new EFDB01Context()) 4 { 5 db.Database.ExecuteSqlCommand("truncate table T_UserInfor"); 6 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('01','ypf1','男',1000,'2019-08-08')"); 7 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('02','ypf2','男',1000,'2019-08-08')"); 8 } 9 //事務(wù)1 10 Task.Run(() => 11 { 12 using (var db = new EFDB01Context()) 13 { 14 using (var transaction = db.Database.BeginTransaction()) 15 { 16 try 17 { 18 var data1 = db.T_UserInfor.Find("01"); 19 data1.userAge -= 500; 20 db.SaveChanges(); 21 22 Task.Delay(TimeSpan.FromSeconds(10)).Wait(); 23 24 var data2 = db.T_UserInfor.Find("02"); 25 data2.userAge -= 500; 26 db.SaveChanges(); 27 28 transaction.Commit(); 29 30 } 31 catch (Exception ex) 32 { 33 34 Console.WriteLine(ex.Message); 35 } 36 } 37 } 38 }); 39 //事務(wù)2 40 Task.Run(() => 41 { 42 using (var db = new EFDB01Context()) 43 { 44 //設(shè)置成“讀未提交” 45 using (var transaction = db.Database.BeginTransaction(IsolationLevel.ReadUncommitted)) 46 { 47 try 48 { 49 Task.Delay(TimeSpan.FromSeconds(5)).Wait(); 50 var data1 = db.T_UserInfor.Find("01"); 51 var data2 = db.T_UserInfor.Find("02"); 52 53 Console.WriteLine($"01 userAge is {data1.userAge}"); 54 Console.WriteLine($"02 userAge is {data2.userAge}"); 55 56 } 57 catch (Exception ex) 58 { 59 60 Console.WriteLine(ex.Message); 61 } 62 } 63 } 64 }); 65 } View Code

?

避免臟讀:將事務(wù)2設(shè)置成讀已提交(IsolationLevel.ReadCommitted 或者不設(shè)置,SQLServer默認就是讀已提交),則事務(wù)2需要等待事務(wù)1執(zhí)行完才能讀取,讀出來的兩條數(shù)據(jù)的均為500,即避免了臟讀。

代碼分享

?

1 { 2 //1.事先準備刪除所有數(shù)據(jù),插入兩條指定數(shù)據(jù) 3 using (EFDB01Context db = new EFDB01Context()) 4 { 5 db.Database.ExecuteSqlCommand("truncate table T_UserInfor"); 6 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('01','ypf1','男',1000,'2019-08-08')"); 7 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('02','ypf2','男',1000,'2019-08-08')"); 8 } 9 //事務(wù)1 10 Task.Run(() => 11 { 12 using (var db = new EFDB01Context()) 13 { 14 using (var transaction = db.Database.BeginTransaction()) 15 { 16 try 17 { 18 var data1 = db.T_UserInfor.Find("01"); 19 data1.userAge -= 500; 20 db.SaveChanges(); 21 22 Task.Delay(TimeSpan.FromSeconds(10)).Wait(); 23 24 var data2 = db.T_UserInfor.Find("02"); 25 data2.userAge -= 500; 26 db.SaveChanges(); 27 28 transaction.Commit(); 29 30 } 31 catch (Exception ex) 32 { 33 34 Console.WriteLine(ex.Message); 35 } 36 } 37 } 38 }); 39 //事務(wù)2 40 Task.Run(() => 41 { 42 using (var db = new EFDB01Context()) 43 { 44 //設(shè)置成“讀已提交”,或者不設(shè)置,SQLServer默認就是讀已提交 45 using (var transaction = db.Database.BeginTransaction(IsolationLevel.ReadCommitted)) 46 { 47 try 48 { 49 Task.Delay(TimeSpan.FromSeconds(5)).Wait(); 50 var data1 = db.T_UserInfor.Find("01"); 51 var data2 = db.T_UserInfor.Find("02"); 52 53 Console.WriteLine($"01 userAge is {data1.userAge}"); 54 Console.WriteLine($"02 userAge is {data2.userAge}"); 55 56 } 57 catch (Exception ex) 58 { 59 60 Console.WriteLine(ex.Message); 61 } 62 } 63 } 64 }); 65 } View Code

?

 (2).不可重復(fù)讀測試:事務(wù)1中5s后將01數(shù)據(jù)的userAge的值由1000改為500,事務(wù)2中在“讀已提交”的情況下兩次讀取的01的數(shù)據(jù)分別是1000,500,即為不可重復(fù)讀。

代碼分享

?

1 { 2 { 3 //1.事先準備刪除所有數(shù)據(jù),插入兩條指定數(shù)據(jù) 4 using (EFDB01Context db = new EFDB01Context()) 5 { 6 db.Database.ExecuteSqlCommand("truncate table T_UserInfor"); 7 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('01','ypf1','男',1000,'2019-08-08')"); 8 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('02','ypf2','男',1000,'2019-08-08')"); 9 } 10 //事務(wù)1 11 Task.Run(() => 12 { 13 using (var db = new EFDB01Context()) 14 { 15 using (var transaction = db.Database.BeginTransaction()) 16 { 17 try 18 { 19 Task.Delay(TimeSpan.FromSeconds(5)).Wait(); 20 21 var data1 = db.T_UserInfor.Find("01"); 22 data1.userAge -= 500; 23 db.SaveChanges(); 24 25 transaction.Commit(); 26 27 } 28 catch (Exception ex) 29 { 30 31 Console.WriteLine(ex.Message); 32 } 33 } 34 } 35 }); 36 //事務(wù)2 37 Task.Run(() => 38 { 39 using (var db = new EFDB01Context()) 40 { 41 //設(shè)置成“讀已提交”,或者不設(shè)置,SQLServer默認就是讀已提交 42 using (var transaction = db.Database.BeginTransaction(IsolationLevel.ReadCommitted)) 43 { 44 try 45 { 46 //一定要加上這句,否則下面的第二個Find不讀取數(shù)據(jù)庫 47 db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 48 49 var data1 = db.T_UserInfor.Find("01"); 50 Console.WriteLine($"01 userAge is {data1.userAge}"); 51 52 Task.Delay(TimeSpan.FromSeconds(6)).Wait(); 53 54 var data2 = db.T_UserInfor.Find("01"); 55 Console.WriteLine($"01 userAge is {data2.userAge}"); 56 57 } 58 catch (Exception ex) 59 { 60 61 Console.WriteLine(ex.Message); 62 } 63 } 64 } 65 }); 66 } 67 } View Code

?

避免不可重復(fù)讀:將事務(wù)2設(shè)置成“可重復(fù)讀”(IsolationLevel.RepeatableRead),事務(wù)2兩次讀取的數(shù)據(jù)均為1000,避免了不可重復(fù)讀。(但第二次數(shù)據(jù)和數(shù)據(jù)庫已經(jīng)不一樣了,數(shù)據(jù)庫中是500)

代碼分享

?

1 { 2 { 3 //1.事先準備刪除所有數(shù)據(jù),插入兩條指定數(shù)據(jù) 4 using (EFDB01Context db = new EFDB01Context()) 5 { 6 db.Database.ExecuteSqlCommand("truncate table T_UserInfor"); 7 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('01','ypf1','男',1000,'2019-08-08')"); 8 db.Database.ExecuteSqlCommand("insert into T_UserInfor values('02','ypf2','男',1000,'2019-08-08')"); 9 } 10 //事務(wù)1 11 Task.Run(() => 12 { 13 using (var db = new EFDB01Context()) 14 { 15 using (var transaction = db.Database.BeginTransaction()) 16 { 17 try 18 { 19 Task.Delay(TimeSpan.FromSeconds(5)).Wait(); 20 21 var data1 = db.T_UserInfor.Find("01"); 22 data1.userAge -= 500; 23 db.SaveChanges(); 24 25 transaction.Commit(); 26 27 } 28 catch (Exception ex) 29 { 30 31 Console.WriteLine(ex.Message); 32 } 33 } 34 } 35 }); 36 //事務(wù)2 37 Task.Run(() => 38 { 39 using (var db = new EFDB01Context()) 40 { 41 //設(shè)置成“可重復(fù)讀” 42 using (var transaction = db.Database.BeginTransaction(IsolationLevel.RepeatableRead)) 43 { 44 try 45 { 46 //一定要加上這句,否則下面的第二個Find不讀取數(shù)據(jù)庫 47 db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 48 49 var data1 = db.T_UserInfor.Find("01"); 50 Console.WriteLine($"01 userAge is {data1.userAge}"); 51 52 Task.Delay(TimeSpan.FromSeconds(6)).Wait(); 53 54 var data2 = db.T_UserInfor.Find("01"); 55 Console.WriteLine($"01 userAge is {data2.userAge}"); 56 57 } 58 catch (Exception ex) 59 { 60 61 Console.WriteLine(ex.Message); 62 } 63 } 64 } 65 }); 66 } 67 } View Code

?

 (3).幻讀測試

有點問題,需要在什么場景下測試??

?

三. 死鎖

?

? ?詳見開頭之前的章節(jié),此處不再重復(fù)介紹了

?

?

!

  • 作???????者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲?????明1 : 本人才疏學(xué)淺,用郭德綱的話說“我是一個小學(xué)生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲?????明2 : 原創(chuàng)博客請在轉(zhuǎn)載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責(zé)任的權(quán)利。

轉(zhuǎn)載于:https://www.cnblogs.com/yaopengfei/p/11394728.html

總結(jié)

以上是生活随笔為你收集整理的第六节:深究事务的相关性质、隔离级别及对应的问题、死锁相关的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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