第六節:深究事務的相關性質、隔離級別及對應的問題、死鎖相關

一. 相關概念?

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

1. 什么是事務

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

2. 事務的特征

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

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

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

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

  (4).持久性:事務的持久性是指事務一旦提交后,數據庫中的數據必須被永久的保存下來。即使服務器系統崩潰或服務器宕機等故障。只要數據庫重新啟動,那么一定能夠將其恢復到事務成功結束后的狀態。

二. 事務隔離級別以及引發的問題

1. 隔離級別

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

  【引發的問題:臟讀】

 (2).讀已提交(READ_COMMITTED) :事務執行的時候只能獲取到其它事務已經提交的數據,獲取不到未提交的數據。

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

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

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

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

  【解決上述所有情況】

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

PS:常見數據庫的默認級別:

 ①:MySQL 數據庫的默認隔離級別是 Repeatable read 級別。

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

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

2.引發的問題

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

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

 (3).幻讀(Phantom Read):指同樣的事務操作,在前后兩個時間段內執行對同一個數據項的讀取,可能出現不一致的結果集。

3. 案例測試

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

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

代碼分享

?

 1             {
 2                 //1.事先準備刪除所有數據,插入兩條指定數據
 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                 //事務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                 //事務2
40                 Task.Run(() =>
41                 {
42                     using (var db = new EFDB01Context())
43                     {
44                         //設置成“讀未提交”
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

?

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

代碼分享

?

 1   {
 2                 //1.事先準備刪除所有數據,插入兩條指定數據
 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                 //事務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                 //事務2
40                 Task.Run(() =>
41                 {
42                     using (var db = new EFDB01Context())
43                     {
44                         //設置成“讀已提交”,或者不設置,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).不可重復讀測試:事務1中5s后將01數據的userAge的值由1000改為500,事務2中在“讀已提交”的情況下兩次讀取的01的數據分別是1000,500,即為不可重復讀。

代碼分享

?

 1   {
 2                 {
 3                     //1.事先準備刪除所有數據,插入兩條指定數據
 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                     //事務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                     //事務2
37                     Task.Run(() =>
38                     {
39                         using (var db = new EFDB01Context())
40                         {
41                             //設置成“讀已提交”,或者不設置,SQLServer默認就是讀已提交
42                             using (var transaction = db.Database.BeginTransaction(IsolationLevel.ReadCommitted))
43                             {
44                                 try
45                                 {
46                                     //一定要加上這句,否則下面的第二個Find不讀取數據庫
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

?

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

代碼分享

?

 1  {
 2                 {
 3                     //1.事先準備刪除所有數據,插入兩條指定數據
 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                     //事務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                     //事務2
37                     Task.Run(() =>
38                     {
39                         using (var db = new EFDB01Context())
40                         {
41                             //設置成“可重復讀”
42                             using (var transaction = db.Database.BeginTransaction(IsolationLevel.RepeatableRead))
43                             {
44                                 try
45                                 {
46                                     //一定要加上這句,否則下面的第二個Find不讀取數據庫
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).幻讀測試

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

?

三. 死鎖

?

? ?詳見開頭之前的章節,此處不再重復介紹了

?

?

!

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

轉載于:https://www.cnblogs.com/yaopengfei/p/11394728.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248209.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248209.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248209.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

js(Dom+Bom)第五天(2)

webAPI 01-事件監聽 為什么要學事件監聽 之前給元素注冊事件的時候,同一個事件會被覆蓋掉事件監聽的本質 通過另外一種方式給元素注冊事件, 同時可以解決同一個事件不會被覆蓋掉.知識點-通過 on 方式給元素注冊事件 之前注冊事件的方式:事件源.onclick function() {}on是一種…

UIAutomator簡介

簡介 Android 4.3發布的時候包含了一種新的測試工具–uiautomator,uiautomator是用來做UI測試的。也就是普通的手工測試,點擊每個控件元素 看看輸出的結果是否符合預期。比如 登陸界面 分別輸入正確和錯誤的用戶名密碼然后點擊登陸按鈕看看是否能否登陸以…

從零開始實現ASP.NET Core MVC的插件式開發(五) - 插件的刪除和升級

標題:從零開始實現ASP.NET Core MVC的插件式開發(五) - 使用AssemblyLoadContext實現插件的升級和刪除 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11395828.html 源代碼:https://github.com/lamondlu/Mystique 前景回…

JDBC Druid式link

準備工作:導入包------druid-1.0.9.jar src文件夾下放下druid.properties文件 且其中的url和數據庫名要配置完備 import JdbcUtils.JDBCUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.PseudoColumnUsage;import java.sql…

vs strcore.cpp(156) 內存泄漏

在一個靜態函數的線程回調中,一個cstring的對象沒釋放。 遇到這種問題: 1、查看所有的指針對象,有沒有合適的回收 2、查看代碼的malloc,看看有沒有free 3、一點一點注釋代碼,定位位置 轉載于:https://www.cnblogs.com/…

js(Dom+Bom)第六天(1)

webAPI 01-鍵盤事件 知識點-onkeydown事件[掌握] onkeydown: 當鍵盤上的鍵被按下時候觸發的一個事件知識點-onkeyup事件[掌握] onkeyup: 鍵盤上鍵彈起時候觸發的一個事件知識點-onkeypress事件[了解] onkeypress: 當鍵盤上的鍵被按下時候觸發的事件onkeydown 和 onkeypress的區…

【JavaWeb】石家莊地鐵搭乘系統——第一版(功能尚未完善)

小組成員:高達,程超然 項目目的:能算出地鐵搭乘的最優路線并顯示在地圖上 個人任務:調用隊友寫好的java代碼,將結果顯示在網頁上 新的知識:百度地圖API,使用JQuery的AJAX異步提交 進度&#xff…

掃描槍連接zebra打印機打印條碼標簽無需電腦

在一些流水線生產的現場,需要及時打印條碼標簽,由于現場環境和空間限制,無法擺放電腦或者通過連接電腦來打印條碼標簽的速度太慢, 瑞科條碼特提供了一套掃描槍直接連接條碼打印機,掃描槍掃描條碼之后直接打印輸出條碼標…

簡單的動畫函數封裝(1)

//創建簡單的動畫函數封裝效果(目標對象&#xff0c;目標位置) function animate(obj,target){var id setInterval(function(){if(obj.offsetLeft > target){clearInterval(id);}else{obj.style.left obj.offsetLeft 5 px;}},30) }可以實現如下效果&#xff1a; <div…

NodeMCU學習(三) : 進入網絡世界

閱讀原文可以訪問我的個人博客 把NodeMCU連接到路由器網絡上 NodeMCU可以被配置為Station模式和softAP模式或者Station AP模式&#xff0c;當它被配置為Station模式時&#xff0c;就可以去連接Access Point&#xff08;如路由器&#xff09;。當它被配置為Soft Access Point模…

操作系統原理之進程調度與死鎖(三)

一、進程調度的功能與時機 進程調度&#xff1a;進程調度的功能由操作系統的進程調度程序完成 具體任務&#xff1a;按照某種策略和算法從就緒態進程中為當前空閑的CPU選擇在其上運行的新進程。 進程調度的時機&#xff1a;進程正常或異常結束、進程阻塞、有更高優先級進程到來…

模擬京東側邊欄

思路&#xff1a; // 1. 獲取元素 // 1.1 獲取一組li // 1.2 獲取一組類名為item的div // 1.3 獲取類名為slide的div// 2. 循環遍歷給每一個li注冊onmouseenter&#xff0c;并且每一個li添加一個index表示索引 // 2.1 循環遍歷把所有的li的類名設置為,把所有的item的display設…

ViewPager + TabLayout + Fragment + MediaPlayer的使用

效果圖 在gradle里導包 implementation com.android.support:design:28.0.0 activity_main <?xml version"1.0" encoding"utf-8"?><LinearLayout xmlns:android"http://schemas.android.com/apk/res/android" xmlns:tools"http…

vs code打開文件顯示的中文亂碼

這種情況下&#xff0c;一般是編碼格式導致的&#xff0c;操作辦法&#xff1a; 鼠標點擊之后&#xff0c;上面會彈出這個界面&#xff0c;雙擊選中 然后從UTF-8換到GB2312&#xff0c;或者自己根據情況&#xff0c;更改編碼格式 轉載于:https://www.cnblogs.com/132818Creator…

操作系統原理之內存管理(第四章第一部分)

內存管理的?標&#xff1a;實現內存分配和回收&#xff0c;提高內存空間的利用率和內存的訪問速度 一、存儲器的層次結構 寄存器&#xff1a;在CPU內部有一組CPU寄存器&#xff0c;寄存器是cpu直接訪問和處理的數據&#xff0c;是一個臨時放數據的空間。 高速緩沖區&#xff1…

自寫圖片遮罩層放大功能jquery插件源代碼,photobox.js 1.0版,不兼容IE6

版權聲明&#xff1a;本文為博主原創文章。未經博主同意不得轉載。 https://blog.csdn.net/u010480479/article/details/27362147 阿嚏~~~ 話說本屌絲沒啥開發插件的經驗&#xff0c;可是天公不作美&#xff0c;公司須要讓我自己開發個圖片放大的插件 但公司老大的話&#xff0…

黑白兩客進入頁面(1)

<div><span>歡</span><span>迎</span><span>來</span><span>到</span><span><strong>黑白兩客</strong></span><span>的</span><span>博</span><span>客</sp…

zookeeper學習之原理

一、zookeeper 是什么 Zookeeper是一個分布式協調服務&#xff0c;可用于服務發現&#xff0c;分布式鎖&#xff0c;分布式領導選舉&#xff0c;配置管理等。這一切的基礎&#xff0c;都是Zookeeper提供了一個類似于Linux文件系統的樹形結構&#xff08;可認為是輕量級的內存文…

mysql鎖表查詢和解鎖操作

解除正在死鎖的狀態有兩種方法&#xff1a; 第一種&#xff1a; 1.查詢是否鎖表 show OPEN TABLES where In_use > 0; 2.查詢進程&#xff08;如果您有SUPER權限&#xff0c;您可以看到所有線程。否則&#xff0c;您只能看到您自己的線程&#xff09; show processlist 3.殺…

前端js基礎智能機器人

<script>var flag true;while(flag) {//獲取用戶輸入信息 var code prompt(你好,我是小娜\n請輸入編號或者關鍵詞選擇功能,輸入Q(q)退出聊天\n1.計算\n2.時間\n3.笑話);switch( code ) {case q:case Q:alert(狠心的拋棄了小娜);flag false;break;case 1:case 計算:var…