中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C# 中怎么解決數據庫并發

發布時間:2021-07-07 15:08:15 來源:億速云 閱讀:256 作者:Leah 欄目:大數據

這期內容當中小編將會給大家帶來有關C# 中怎么解決數據庫并發,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

數據庫并發的處理方式有哪些呢?

其實數據庫的并發處理也是分為樂觀鎖和悲觀鎖,只不過是基于數據庫層面而言的!關于數據庫層面的并發處理大家可參考我的博客:樂觀鎖悲觀鎖應用

悲觀鎖:假定會發生并發沖突,屏蔽一切可能違反數據完整性的操作。[1]

樂觀鎖:假設不會發生并發沖突,只在提交操作時檢查是否違反數據完整性。[1] 樂觀鎖不能解決臟讀的問題。

 最常用的處理多用戶并發訪問的方法是加鎖。當一個用戶鎖住數據庫中的某個對象時,其他用戶就不能再訪問該對象。加鎖對并發訪問的影響體現在鎖的粒度上。比如,放在一個表上的鎖限制對整個表的并發訪問;放在數據頁上的鎖限制了對整個數據頁的訪問;放在行上的鎖只限制對該行的并發訪問。可見行鎖粒度最小,并發訪問最好,頁鎖粒度最大,并發訪問性能就會越低。

悲觀鎖:假定會發生并發沖突,屏蔽一切可能違反數據完整性的操作。[1] 悲觀鎖假定其他用戶企圖訪問或者改變你正在訪問、更改的對象的概率是很高的,因此在悲觀鎖的環境中,在你開始改變此對象之前就將該對象鎖住,并且直到你提交了所作的更改之后才釋放鎖。悲觀的缺陷是不論是頁鎖還是行鎖,加鎖的時間可能會很長,這樣可能會長時間的鎖定一個對象,限制其他用戶的訪問,也就是說悲觀鎖的并發訪問性不好。

樂觀鎖:假設不會發生并發沖突,只在提交操作時檢查是否違反數據完整性。[1] 樂觀鎖不能解決臟讀的問題。 樂觀鎖則認為其他用戶企圖改變你正在更改的對象的概率是很小的,因此樂觀鎖直到你準備提交所作的更改時才將對象鎖住,當你讀取以及改變該對象時并不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖可以用較大的鎖粒度獲得較好的并發訪問性能。但是如果第二個用戶恰好在第一個用戶提交更改之前讀取了該對象,那么當他完成了自己的更改進行提交時,數據庫就會發現該對象已經變化了,這樣,第二個用戶不得不重新讀取該對象并作出更改。這說明在樂觀鎖環境中,會增加并發用戶讀取對象的次數。

本篇的主旨是講解基于C#的數據庫并發解決方案(通用版、EF版),因此我們要從C#方面入手,最好是結合一個小項目

項目已為大家準備好了,如下:

首先我們需要創建一個小型數據庫:

C# 中怎么解決數據庫并發

復制代碼

create database  BingFaTest
go
use BingFaTest
go 
create table Product--商品表
(
ProductId int identity(1,1) primary key,--商品ID 主鍵
ProductName nvarchar(50),--商品名稱
ProductPrice money,--單價
ProductUnit nvarchar(10) default('元/斤'),
AddTime datetime default(getdate())--添加時間

)


create table Inventory--庫存表
(
InventoryId int identity(1,1) primary key,
ProductId int FOREIGN KEY REFERENCES Product(ProductId), --外鍵
ProductCount int,--庫存數量
VersionNum TimeStamp not null,
InventoryTime datetime default(getdate()),--時間
)

create table InventoryLog
(
Id int identity(1,1) primary key,
Title nvarchar(50),
)


--測試數據:
insert into Product values('蘋果',1,'元/斤',GETDATE())


insert into Inventory(ProductId,ProductCount,InventoryTime) values(1,100,GETDATE())

C# 中怎么解決數據庫并發

創建的數據庫很簡單,三張表:商品表,庫存表,日志表

有了數據庫,我們就創建C#項目,本項目采用C# DataBaseFirst 模式,結構如下:

C# 中怎么解決數據庫并發

#region 未做并發處理
        /// <summary>
        /// 模仿一個減少庫存操作  不加并發控制
        /// </summary>
        public void SubMitOrder_3()
        {
            int productId = 1;

            using (BingFaTestEntities context = new BingFaTestEntities())
            {
                var InventoryLogDbSet = context.InventoryLog;
                var InventoryDbSet = context.Inventory;//庫存表

                using (var Transaction = context.Database.BeginTransaction())
                {
                    //減少庫存操作
                    var Inventory_Mol = InventoryDbSet.Where(A => A.ProductId == productId).FirstOrDefault();//庫存對象
                    Inventory_Mol.ProductCount = Inventory_Mol.ProductCount - 1;
                    int A4 = context.SaveChanges();
                    //插入日志
                    InventoryLog LogModel = new InventoryLog()
                    {
                        Title = "插入一條數據,用于計算是否發生并發",

                    };
                    InventoryLogDbSet.Add(LogModel);
                    context.SaveChanges();
                    //1.5  模擬耗時
                    Thread.Sleep(500); //消耗半秒鐘
                    Transaction.Commit();
                }

            }
        }
        #endregion

C# 中怎么解決數據庫并發

由上圖可知,四個訪問者同時訪問這個未采用并發控制的方法,得到的結果如下:

C# 中怎么解決數據庫并發

樂觀者方法(通用版/存儲過程實現):

在上述數據庫腳本中,有字段叫做:VersionNum,類型為:TimeStamp。

字段 VersionNum 大家可以理解為版本號,版本號的作用是一旦有訪問者修改數據,版本號的值就會相應發生改變。當然,版本號的同步更改是和數據庫相關的,在SQLserver中會隨著數據的修改同步更新版本號,但是在MySQL里就不會隨著數據的修改而更改。因此,如果你采用的是MYSQL數據庫,就需要寫一個觸發器,如下:

C# 中怎么解決數據庫并發

OK,了解了類型為Timestamp的字段,下面我們結合上述的小型數據庫創建一個處理并發的存儲過程,如下

復制代碼

create proc LockProc --樂觀鎖控制并發
(
@ProductId int, 
@IsSuccess bit=0 output
)
as
declare @count as int
declare @flag as TimeStamp
declare @rowcount As int 
begin tran
select @count=ProductCount,@flag=VersionNum from Inventory where ProductId=@ProductId
 
update Inventory set ProductCount=@count-1 where VersionNum=@flag and ProductId=@ProductId
insert into InventoryLog values('插入一條數據,用于計算是否發生并發')
set @rowcount=@@ROWCOUNT
if @rowcount>0
set @IsSuccess=1
else
set @IsSuccess=0
commit tran

C# 中怎么解決數據庫并發

 這個存儲過程很簡單,執行兩個操作:減少庫存和插入一條數據。有一個輸入參數:productId ,一個輸出參數,IsSuccess。如果發生并發,IsSuccess的值為False,如果執行成功,IsSuccess值為True。

在這里,向大家說明一點:程序采用悲觀鎖,是串行的,采用樂觀鎖,是并行的。

也就是說:采用悲觀鎖,一次僅執行一個訪問者的請求,待前一個訪問者訪問完成并釋放鎖時,下一個訪問者會依次進入鎖定的程序并執行,直到所有訪問者執行結束。因此,悲觀鎖嚴格按照次序執行的模式能保證所有訪問者執行成功。

采用樂觀鎖時,訪問者是并行執行的,大家同時訪問一個方法,只不過同一時刻只會有一個訪問者操作成功,其他訪問者執行失敗。那么,針對這些執行失敗的訪問者怎么處理呢?直接返回失敗信息是不合理的,用戶體驗不好,因此,需要定制一個規則,讓執行失敗的訪問者重新執行之前的請求即可。

 時間有限,就不多寫了...因為并發的控制是在數據庫端存儲過程,所以,C#代碼也很簡單。如下:

C# 中怎么解決數據庫并發

#region 通用并發處理模式 存儲過程實現
        /// <summary>
        /// 存儲過程實現
        /// </summary>
        public void SubMitOrder_2()
        {
            int productId = 1;
            bool bol = LockForPorcduce(productId);
            //1.5  模擬耗時
            Thread.Sleep(500); //消耗半秒鐘
            int retry = 10;
            while (!bol && retry > 0)
            {
                retry--;
                LockForPorcduce(productId);
            }
        }


        private bool LockForPorcduce(int ProductId)
        {
            using (BingFaTestEntities context = new BingFaTestEntities())
            {
                SqlParameter[] parameters = {
                    new SqlParameter("@ProductId", SqlDbType.Int),
                    new SqlParameter("@IsSuccess", SqlDbType.Bit)
                    };
                parameters[0].Value = ProductId;
                parameters[1].Direction = ParameterDirection.Output;
                var data = context.Database.ExecuteSqlCommand("exec LockProc @ProductId,@IsSuccess output", parameters);
                string n2 = parameters[1].Value.ToString();
                if (n2 == "True")
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        #endregion

C# 中怎么解決數據庫并發

Edmx文件用記事本打開如下:

C# 中怎么解決數據庫并發

設置好了版本號屬性后,你就可以進行并發測試了,當系統發生并發時,程序會拋出異常,而我們要做的就是要捕獲這個異常,而后就是按照自己的規則,重復執行請求的方法,直至返回成功為止。

那么如何捕獲并發異常呢?

在C#代碼中需要使用異常類:DbUpdateConcurrencyException 來捕獲,EF中具體用法如下:

復制代碼

public class SaveChangesForBF : BingFaTestEntities
    {
        public override int SaveChanges()
        {
            try
            {
                return base.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)//(OptimisticConcurrencyException)
            {
                //并發保存錯誤
                return -1;
            }
        }
    }

C# 中怎么解決數據庫并發

設置好屬性后,EF會幫我們自動檢測并發并拋出異常,我們用上述方法捕獲異常后,就可以執行我們重復執行的規則了,具體代碼如下:

C# 中怎么解決數據庫并發

#region EF專屬并發處理模式
        /// <summary>
        /// 存儲過程實現
        /// </summary>
        public void SubMitOrder()
        {
            int C = LockForEF();
            //1.5  模擬耗時
            Thread.Sleep(500); //消耗半秒鐘
            int retry = 10;
            while (C<0 && retry > 0)
            {
                retry--;
                C= LockForEF();
            }
        }
        /// <summary>
        /// 模仿一個減少庫存操作  EF專屬并發處理模式
        /// </summary>
        public int LockForEF()
        {
            int productId = 1;
            int C = 0;
            using (SaveChangesForBF context = new SaveChangesForBF())
            {
                var InventoryLogDbSet = context.InventoryLog;
                var InventoryDbSet = context.Inventory;//庫存表

                using (var Transaction = context.Database.BeginTransaction())
                {
                    //減少庫存操作
                    var Inventory_Mol = InventoryDbSet.Where(A => A.ProductId == productId).FirstOrDefault();//庫存對象
                    Inventory_Mol.ProductCount = Inventory_Mol.ProductCount - 1;
                    C = context.SaveChanges();
                    //插入日志
                    InventoryLog LogModel = new InventoryLog()
                    {
                        Title = "插入一條數據,用于計算是否發生并發",
                        
                    };
                    InventoryLogDbSet.Add(LogModel);
                    context.SaveChanges();
                    //1.5  模擬耗時
                    Thread.Sleep(500); //消耗半秒鐘
                    Transaction.Commit();
                }

            }
            return C;
        }
        #endregion

上述就是小編為大家分享的C# 中怎么解決數據庫并發了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

堆龙德庆县| 永州市| 沁水县| 盐源县| 衡水市| 洛浦县| 武陟县| 千阳县| 德安县| 通榆县| 红安县| 鄂伦春自治旗| 龙江县| 格尔木市| 瓮安县| 长乐市| 渝中区| 锦州市| 博罗县| 白朗县| 清涧县| 武强县| 婺源县| 阳信县| 莒南县| 张家界市| 同德县| 无极县| 杭锦后旗| 金山区| 新乡市| 秭归县| 铁岭县| 南木林县| 钟祥市| 丽水市| 阳城县| 湾仔区| 金沙县| 江山市| 东方市|