真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法

這篇文章主要介紹“MySQL數(shù)據(jù)庫(kù)端處理并發(fā)的方法”,在日常操作中,相信很多人在mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、鐵西網(wǎng)絡(luò)推廣、成都小程序開發(fā)、鐵西網(wǎng)絡(luò)營(yíng)銷、鐵西企業(yè)策劃、鐵西品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供鐵西建站搭建服務(wù),24小時(shí)服務(wù)熱線:18980820575,官方網(wǎng)址:www.cdcxhl.com

自ASP.NET誕生以來,微軟提供了不少控制并發(fā)的方法,在了解這些控制并發(fā)的方法前,我們先來簡(jiǎn)單介紹下并發(fā)!

并發(fā):同一時(shí)間或者同一時(shí)刻多個(gè)訪問者同時(shí)訪問某一更新操作時(shí),會(huì)產(chǎn)生并發(fā)!

針對(duì)并發(fā)的處理,又分為悲觀并發(fā)處理和樂觀并發(fā)處理

所謂悲觀/樂觀并發(fā)處理,可以這樣理解:

悲觀者認(rèn)為:在程序的運(yùn)行過程中,并發(fā)很容易發(fā)生滴,因此,悲觀者提出了他們的處理模式:在我執(zhí)行一個(gè)方法時(shí),不允許其他訪問者介入這個(gè)方法。(悲觀者經(jīng)常認(rèn)為某件壞事會(huì)發(fā)生在自己身上)

樂觀者認(rèn)為:在程序的運(yùn)行過程中,并發(fā)是很少發(fā)生滴,因此,樂觀者提出了他們的處理模式:在我執(zhí)行一個(gè)方法時(shí),允許其他訪問者介入這個(gè)方法。(樂觀者經(jīng)常認(rèn)為某件壞事不會(huì)發(fā)生在自己身上)

那么在C#語言中,那些屬于悲觀者呢?

在C#中諸如:LOCK、Monitor、Interlocked 等鎖定數(shù)據(jù)的方式,屬于悲觀并發(fā)處理范疇!數(shù)據(jù)一旦被鎖定,其他訪問者均無權(quán)訪問。有興趣的可以參考:鎖、C#中Monitor和Lock以及區(qū)別

但是,悲觀者處理并發(fā)的模式有一個(gè)通病,那就是可能會(huì)造成非常低下的執(zhí)行效率。

在此:舉個(gè)簡(jiǎn)單例子:

售票系統(tǒng),小明去買票,要買北京到上海的D110次列車,如果采用悲觀者處理并發(fā)的模式,那么售票員會(huì)將D110次列車的票鎖定,然后再作出票操作。但是,在D110次列車車票被鎖定期間,售票員去了趟廁所,或者喝了杯咖啡,其他窗口售票員是不能進(jìn)行售票滴!如果采用這種處理方式的話,中國(guó)14億人口都不用出行了,原因是買不到票 ~_~

因此:在處理數(shù)據(jù)庫(kù)并發(fā)時(shí),悲觀鎖還是要謹(jǐn)慎使用!具體還要看數(shù)據(jù)庫(kù)并發(fā)量大不大,如果比較大,建議使用樂觀者處理模式,如果比較小,可以適當(dāng)采用悲觀者處理模式!

OK。說了這么多,也就是做個(gè)鋪墊,本節(jié)內(nèi)容標(biāo)題叫數(shù)據(jù)庫(kù)并發(fā)的解決方案,我們最終還得返璞歸真,從數(shù)據(jù)庫(kù)并發(fā)的解決說起!

那么問題來了?

數(shù)據(jù)庫(kù)并發(fā)的處理方式有哪些呢?

其實(shí)數(shù)據(jù)庫(kù)的并發(fā)處理也是分為樂觀鎖和悲觀鎖,只不過是基于數(shù)據(jù)庫(kù)層面而言的!關(guān)于數(shù)據(jù)庫(kù)層面的并發(fā)處理大家可參考我的博客:樂觀鎖悲觀鎖應(yīng)用

悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。[1]

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

 最常用的處理多用戶并發(fā)訪問的方法是加鎖。當(dāng)一個(gè)用戶鎖住數(shù)據(jù)庫(kù)中的某個(gè)對(duì)象時(shí),其他用戶就不能再訪問該對(duì)象。加鎖對(duì)并發(fā)訪問的影響體現(xiàn)在鎖的粒度上。比如,放在一個(gè)表上的鎖限制對(duì)整個(gè)表的并發(fā)訪問;放在數(shù)據(jù)頁上的鎖限制了對(duì)整個(gè)數(shù)據(jù)頁的訪問;放在行上的鎖只限制對(duì)該行的并發(fā)訪問??梢娦墟i粒度最小,并發(fā)訪問最好,頁鎖粒度最大,并發(fā)訪問性能就會(huì)越低。

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

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

本篇的主旨是講解基于C#的數(shù)據(jù)庫(kù)并發(fā)解決方案(通用版、EF版),因此我們要從C#方面入手,最好是結(jié)合一個(gè)小項(xiàng)目

項(xiàng)目已為大家準(zhǔn)備好了,如下:

首先我們需要?jiǎng)?chuàng)建一個(gè)小型數(shù)據(jù)庫(kù):

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

)


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

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


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


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

創(chuàng)建的數(shù)據(jù)庫(kù)很簡(jiǎn)單,三張表:商品表,庫(kù)存表,日志表

有了數(shù)據(jù)庫(kù),我們就創(chuàng)建C#項(xiàng)目,本項(xiàng)目采用C# DataBaseFirst 模式,結(jié)構(gòu)如下:

mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法

項(xiàng)目很簡(jiǎn)單,采用EF DataBaseFirst 模式很好構(gòu)建。

 項(xiàng)目構(gòu)建好了,下面我們模擬并發(fā)的發(fā)生?

主要代碼如下(減少庫(kù)存、插入日志):

#region 未做并發(fā)處理
        /// 
        /// 模仿一個(gè)減少庫(kù)存操作  不加并發(fā)控制
        /// 
        public void SubMitOrder_3()
        {
            int productId = 1;

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

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

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

            }
        }
        #endregion

此時(shí)我們 int productId=1 處加上斷點(diǎn),并運(yùn)行程序(打開四個(gè)瀏覽器同時(shí)執(zhí)行),如下:

mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法

結(jié)果顯示:日志生成四條數(shù)據(jù),而庫(kù)存量缺只減少1個(gè)。這個(gè)結(jié)果顯然是不正確的,原因是因?yàn)榘l(fā)生了并發(fā),其本質(zhì)原因是臟讀,誤讀,不可重讀造成的。

那么,問題既然發(fā)生了,我們就想辦法法解決,辦法有兩種,分別為:悲觀鎖方法、樂觀鎖方法。

悲觀者方法:

悲觀者方法(加了uodlock鎖,鎖定了更新操作,也就是說,一旦被鎖定,其他訪問者不允許訪問此操作)類似這種方法,可以通過存儲(chǔ)過程實(shí)現(xiàn),在此不作解釋了

mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法

OK,了解了類型為Timestamp的字段,下面我們結(jié)合上述的小型數(shù)據(jù)庫(kù)創(chuàng)建一個(gè)處理并發(fā)的存儲(chǔ)過程,如下

create proc LockProc --樂觀鎖控制并發(fā)
(
@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('插入一條數(shù)據(jù),用于計(jì)算是否發(fā)生并發(fā)')
set @rowcount=@@ROWCOUNT
if @rowcount>0
set @IsSuccess=1
else
set @IsSuccess=0
commit tran

 這個(gè)存儲(chǔ)過程很簡(jiǎn)單,執(zhí)行兩個(gè)操作:減少庫(kù)存和插入一條數(shù)據(jù)。有一個(gè)輸入?yún)?shù):productId ,一個(gè)輸出參數(shù),IsSuccess。如果發(fā)生并發(fā),IsSuccess的值為False,如果執(zhí)行成功,IsSuccess值為True。

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

也就是說:采用悲觀鎖,一次僅執(zhí)行一個(gè)訪問者的請(qǐng)求,待前一個(gè)訪問者訪問完成并釋放鎖時(shí),下一個(gè)訪問者會(huì)依次進(jìn)入鎖定的程序并執(zhí)行,直到所有訪問者執(zhí)行結(jié)束。因此,悲觀鎖嚴(yán)格按照次序執(zhí)行的模式能保證所有訪問者執(zhí)行成功。

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

 時(shí)間有限,就不多寫了...因?yàn)椴l(fā)的控制是在數(shù)據(jù)庫(kù)端存儲(chǔ)過程,所以,C#代碼也很簡(jiǎn)單。如下:

#region 通用并發(fā)處理模式 存儲(chǔ)過程實(shí)現(xiàn)
        /// 
        /// 存儲(chǔ)過程實(shí)現(xiàn)
        /// 
        public void SubMitOrder_2()
        {
            int productId = 1;
            bool bol = LockForPorcduce(productId);
            //1.5  模擬耗時(shí)
            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

在此,需要說明如下:

當(dāng)IsSuccess的值為False時(shí),應(yīng)該重復(fù)執(zhí)行該方法,我定的規(guī)則是重復(fù)請(qǐng)求十次,這樣就很好的解決了直接反饋給用戶失敗的消息。提高了用戶體驗(yàn)。

下面著重說下EF框架如何避免數(shù)據(jù)庫(kù)并發(fā),在講解之前,先允許我引用下別人博客中的幾段話:

在軟件開發(fā)過程中,并發(fā)控制是確保及時(shí)糾正由并發(fā)操作導(dǎo)致的錯(cuò)誤的一種機(jī)制。從 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework,.NET 都為并發(fā)控制提供好良好的支持方案。

相對(duì)于數(shù)據(jù)庫(kù)中的并發(fā)處理方式,Entity Framework 中的并發(fā)處理方式實(shí)現(xiàn)了不少的簡(jiǎn)化。

在System.Data.Metadata.Edm 命名空間中,存在ConcurencyMode 枚舉,用于指定概念模型中的屬性的并發(fā)選項(xiàng)。
ConcurencyMode 有兩個(gè)成員:

成員名稱 說明
        None  在寫入時(shí)從不驗(yàn)證此屬性。 這是默認(rèn)的并發(fā)模式。
        Fixed在寫入時(shí)始終驗(yàn)證此屬性。

當(dāng)模型屬性為默認(rèn)值 None 時(shí),系統(tǒng)不會(huì)對(duì)此模型屬性進(jìn)行檢測(cè),當(dāng)同一個(gè)時(shí)間對(duì)此屬性進(jìn)行修改時(shí),系統(tǒng)會(huì)以數(shù)據(jù)合并方式處理輸入的屬性值。
當(dāng)模型屬性為Fixed 時(shí),系統(tǒng)會(huì)對(duì)此模型屬性進(jìn)行檢測(cè),當(dāng)同一個(gè)時(shí)間對(duì)屬性進(jìn)行修改時(shí),系統(tǒng)就會(huì)激發(fā)OptimisticConcurrencyException 異常。

開發(fā)人員可以為對(duì)象的每個(gè)屬性定義不同的 ConcurencyMode 選項(xiàng),選項(xiàng)可以在*.Edmx找看到:

mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法

其實(shí),在EF DataBaseFirst中,我們只需設(shè)置下類型為 TimeStamp 版本號(hào)的屬性即可,如下:

設(shè)置好了版本號(hào)屬性后,你就可以進(jìn)行并發(fā)測(cè)試了,當(dāng)系統(tǒng)發(fā)生并發(fā)時(shí),程序會(huì)拋出異常,而我們要做的就是要捕獲這個(gè)異常,而后就是按照自己的規(guī)則,重復(fù)執(zhí)行請(qǐng)求的方法,直至返回成功為止。

那么如何捕獲并發(fā)異常呢?

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

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

設(shè)置好屬性后,EF會(huì)幫我們自動(dòng)檢測(cè)并發(fā)并拋出異常,我們用上述方法捕獲異常后,就可以執(zhí)行我們重復(fù)執(zhí)行的規(guī)則了,具體代碼如下:

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

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

            }
            return C;
        }
        #endregion

====================自己的,程序處理并發(fā)==================================

使用async/await,創(chuàng)建異步函數(shù),創(chuàng)建一個(gè)子線程,使用await依次執(zhí)行完成

到此,關(guān)于“mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!


網(wǎng)站名稱:mysql數(shù)據(jù)庫(kù)端處理并發(fā)的方法
文章源于:http://weahome.cn/article/pejhco.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部