基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題_第1頁
基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題_第2頁
基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題_第3頁
基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題_第4頁
基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題_第5頁
已閱讀5頁,還剩10頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第基于C#解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題目錄解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題那么怎樣解決死鎖?1.減少事務(wù)的執(zhí)行時(shí)間。2.業(yè)務(wù)鎖測(cè)試場(chǎng)景通過查詢庫存和訂單信息核對(duì)庫存是否扣減正常核驗(yàn)結(jié)果

解決庫存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題

在我們?nèi)粘i_發(fā)的過程可有會(huì)遇到以下錯(cuò)誤

事務(wù)(進(jìn)程ID82)與另一個(gè)進(jìn)程被死鎖在鎖資源上,并且已被選作死鎖犧牲品。請(qǐng)重新運(yùn)行該事務(wù)

很多開發(fā)人員對(duì)于這個(gè)問題的排查起來是比較困難的,而生產(chǎn)生的原因多種多樣,很多人認(rèn)是因?yàn)楸碇械臄?shù)據(jù)太多了同時(shí)操作的人多人才會(huì)產(chǎn)生這種錯(cuò)誤,下面我們來還原一下死鎖的過程。

我們看一下以下sql代碼(該樣例代碼測(cè)試環(huán)境為SqlServer)

1.第一先創(chuàng)建一個(gè)測(cè)試表H_Test

復(fù)制以下代碼

SETANSI_NULLSON

SETQUOTED_IDENTIFIERON

CREATETABLE[dbo].[H_TEST](

[Id][int]IDENTITY(1,1)NOTNULL,

[DID][int]NULL,

[UNAME][nvarchar](50)NULL,

[UNAME2][nvarchar](50)NULL,

CONSTRAINT[PK_H_TEST_3994ceeb-a4b8-41e1-b06b-1e59a2e51d8c]PRIMARYKEYCLUSTERED

[Id]ASC

)WITH(PAD_INDEX=OFF,STATISTICS_NORECOMPUTE=OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS=ON,ALLOW_PAGE_LOCKS=ON)ON[PRIMARY]

)ON[PRIMARY]

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'自增主鍵',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_TEST',@level2type=N'COLUMN',@level2name=N'Id'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'DID',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_TEST',@level2type=N'COLUMN',@level2name=N'DID'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'UNAME',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_TEST',@level2type=N'COLUMN',@level2name=N'UNAME'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'UNAME2',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_TEST',@level2type=N'COLUMN',@level2name=N'UNAME2'

insert[dbo].[H_TEST](DID,UNAME,UNAME2)VALUES(1,'HI','HI2');

insert[dbo].[H_TEST](DID,UNAME,UNAME2)VALUES(2,'HISQL','HISQL2');

2.打開兩個(gè)查詢窗口

在窗口1中復(fù)制以下代碼

begintran

updatedbo.H_TEST

setUNAME='d1'

wheredID=1

waitfordelay'00:00:10'

updateH_TEST

setUNAME='d2'

wheredID=2

committran

在窗口2中復(fù)制以下代碼

begintran

updateH_TEST

setUNAME='d2'

wheredID=2

waitfordelay'00:00:10'

updatedbo.H_TEST

setUNAME='d1'

wheredID=1

committran

3.執(zhí)行代碼

同時(shí)執(zhí)行窗口1和窗口2的代碼,在等待一段時(shí)間后你就可以看到以下錯(cuò)誤如下所示

通過以上的測(cè)試就還原了產(chǎn)生死鎖的過程,剛才的測(cè)試表H_Test中只有兩條數(shù)據(jù),其實(shí)產(chǎn)生死鎖與數(shù)據(jù)大小沒有很大的關(guān)系,其實(shí)與整個(gè)事務(wù)的執(zhí)行長(zhǎng)短有關(guān)系,兩個(gè)業(yè)務(wù)都在操作同一條數(shù)據(jù),且一個(gè)事務(wù)中包含非常復(fù)雜的處理邏輯且執(zhí)行時(shí)間比較長(zhǎng)那么在并發(fā)或相對(duì)較多的業(yè)務(wù)操作時(shí)就會(huì)產(chǎn)生死鎖。

那么怎樣解決死鎖?

1.減少事務(wù)的執(zhí)行時(shí)間。

優(yōu)化代碼將不需要包在事務(wù)的邏輯分離出來以減少鎖的占用時(shí)間.可以減少一部分的死鎖,但在高并發(fā)操作時(shí)依然會(huì)產(chǎn)生死鎖

2.業(yè)務(wù)鎖

日常我們用到的鎖都是高度依賴于數(shù)據(jù)來鎖定來保證數(shù)據(jù)的原子性問題,但這樣有一個(gè)很大的BUG就是對(duì)數(shù)據(jù)庫的性能壓力非常大,在出現(xiàn)高并發(fā)時(shí)可能應(yīng)用扛得住數(shù)據(jù)庫扛不住的情況

下面介紹的就是基于HiSql的業(yè)務(wù)鎖機(jī)制解決死鎖問題,我們模擬一種場(chǎng)景扣減庫存并生成訂單那么我們模擬創(chuàng)建兩張表庫存表H_Stock及訂單表H_Order表創(chuàng)建的sql如下

HiSql怎樣使用請(qǐng)參照hisql快速上手

庫存表sql代碼

CREATETABLE[dbo].[H_Stock](

[Batch][varchar](20)NOTNULL,

[Material][varchar](20)NOTNULL,

[Location][varchar](5)NULL,

[st_kc][decimal](18,2)NULL,

[CreateTime][datetime]NULL,

[CreateName][nvarchar](50)NULL,

[ModiTime][datetime]NULL,

[ModiName][nvarchar](50)NULL,

CONSTRAINT[PK_H_Stock]PRIMARYKEYCLUSTERED

[Batch]ASC,

[Material]ASC

)WITH(PAD_INDEX=OFF,STATISTICS_NORECOMPUTE=OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS=ON,ALLOW_PAGE_LOCKS=ON)ON[PRIMARY]

)ON[PRIMARY]

ALTERTABLE[dbo].[H_Stock]ADDCONSTRAINT[DF_H_Stock_st_kc]DEFAULT((0))FOR[st_kc]

ALTERTABLE[dbo].[H_Stock]ADDCONSTRAINT[DF_H_Stock_CreateTime]DEFAULT(getdate())FOR[CreateTime]

ALTERTABLE[dbo].[H_Stock]ADDCONSTRAINT[DF_H_Stock_CreateName]DEFAULT('')FOR[CreateName]

ALTERTABLE[dbo].[H_Stock]ADDCONSTRAINT[DF_H_Stock_ModiTime]DEFAULT(getdate())FOR[ModiTime]

ALTERTABLE[dbo].[H_Stock]ADDCONSTRAINT[DF_H_Stock_ModiName]DEFAULT('')FOR[ModiName]

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'批次號(hào)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'Batch'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'款號(hào)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'Material'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'庫位',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'Location'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'庫存數(shù)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'st_kc'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'創(chuàng)建時(shí)間',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'CreateTime'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'創(chuàng)建人',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'CreateName'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'修改時(shí)間',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'ModiTime'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'修改人',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Stock',@level2type=N'COLUMN',@level2name=N'ModiName'

訂單表sql

CREATETABLE[dbo].[H_Order](

[OrderId][bigint]NOTNULL,

[Batch][varchar](20)NOTNULL,

[Material][varchar](20)NOTNULL,

[Shop][varchar](5)NULL,

[Location][varchar](5)NULL,

[SalesNum][decimal](18,2)NULL,

[CreateTime][datetime]NULL,

[CreateName][nvarchar](50)NULL,

[ModiTime][datetime]NULL,

[ModiName][nvarchar](50)NULL,

CONSTRAINT[PK_H_Order]PRIMARYKEYCLUSTERED

[OrderId]ASC,

[Batch]ASC,

[Material]ASC

)WITH(PAD_INDEX=OFF,STATISTICS_NORECOMPUTE=OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS=ON,ALLOW_PAGE_LOCKS=ON)ON[PRIMARY]

)ON[PRIMARY]

ALTERTABLE[dbo].[H_Order]ADDCONSTRAINT[DF_H_Order_SalesNum]DEFAULT((0))FOR[SalesNum]

ALTERTABLE[dbo].[H_Order]ADDCONSTRAINT[DF_H_Order_CreateTime]DEFAULT(getdate())FOR[CreateTime]

ALTERTABLE[dbo].[H_Order]ADDCONSTRAINT[DF_H_Order_CreateName]DEFAULT('')FOR[CreateName]

ALTERTABLE[dbo].[H_Order]ADDCONSTRAINT[DF_H_Order_ModiTime]DEFAULT(getdate())FOR[ModiTime]

ALTERTABLE[dbo].[H_Order]ADDCONSTRAINT[DF_H_Order_ModiName]DEFAULT('')FOR[ModiName]

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'批次號(hào)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'Batch'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'款號(hào)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'Material'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'門店',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'Shop'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'出庫庫位',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'Location'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'銷售數(shù)量',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'SalesNum'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'創(chuàng)建時(shí)間',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'CreateTime'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'創(chuàng)建人',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'CreateName'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'修改時(shí)間',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'ModiTime'

EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'修改人',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'H_Order',@level2type=N'COLUMN',@level2name=N'ModiName'

測(cè)試場(chǎng)景

開啟多個(gè)線程隨機(jī)產(chǎn)生不同的訂單(一個(gè)訂單中有不同批次和數(shù)量)直至庫存扣減完成并檢測(cè)是否有鎖產(chǎn)生,且?guī)齑嬗袥]有少扣和超扣,如果達(dá)到這兩個(gè)目標(biāo)說明測(cè)試是成功的

c#代碼

classProgram

staticvoidMain(string[]args)

Console.WriteLine("測(cè)試!");

StockThread();

vars=Console.ReadLine();

staticvoidStockThread()

//如果有安裝redis可以啟用以下測(cè)試一下

//HiSql.Global.RedisOn=true;//開啟redis緩存

//HiSql.Global.RedisOptions=newRedisOptions{Host="78",PassWord="pwd123",Port=6379,CacheRegion="TST",Database=0};

HiSqlClientsqlClient=Demo_Init.GetSqlClient();

//清除庫存表和訂單表數(shù)據(jù)

sqlClient.CodeFirst.Truncate("H_Stock");

sqlClient.CodeFirst.Truncate("H_Order");

//初始化庫存數(shù)據(jù)

sqlClient.Modi("H_Stock",newListobject{

new{Batch="9000112112",Material="ST0021",Location="A001",st_kc=5000},

new{Batch="8000252241",Material="ST0080",Location="A001",st_kc=1000},

new{Batch="7000252241",Material="ST0026",Location="A001",st_kc=1500}

}).ExecCommand();

//第一種場(chǎng)景一個(gè)訂單中只有一個(gè)批次

string[]grp_arr1=newstring[]{"9000112112"};

//第二種場(chǎng)景一個(gè)訂單中有兩個(gè)批次

string[]grp_arr2=newstring[]{"8000252241","9000112112"};

//第三中場(chǎng)景一個(gè)訂單中有三個(gè)批次

string[]grp_arr3=newstring[]{"8000252241","9000112112","7000252241"};

Randomrandom=newRandom();

HiSqlClient_sqlClient=Demo_Init.GetSqlClient();

//表結(jié)構(gòu)緩存預(yù)熱

var_dt1=_sqlClient.HiSql("select*fromH_Order").Take(1).Skip(1).ToTable();

var_dt2=_sqlClient.HiSql("select*fromH_Stock").Take(1).Skip(1).ToTable();

//開啟10個(gè)線程運(yùn)行

Parallel.For(0,10,(index,y)={

intgrpidx=index%3;

string[]grparr=null;

if(grpidx==0)

grparr=grp_arr1;

elseif(grpidx==1)

grparr=grp_arr2;

else

grparr=grp_arr3;

//Thread.Sleep(random.Next(10)*200);

Console.WriteLine($"{index}線程Id:{Thread.CurrentThread.ManagedThreadId}\t{DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss.fff")}");

//執(zhí)行訂單創(chuàng)建

varrtn=CreateSale(grparr);

Console.WriteLine(rtn.Item2);

staticTuplebool,stringCreateSale(string[]grparr)

Randomrandom=newRandom();

HiSqlClient_sqlClient=Demo_Init.GetSqlClient();

bool_flag=true;

Tuplebool,stringrtn=newTuplebool,string(true,"執(zhí)行");

//指定雪花ID使用的引擎(可以不指定)

HiSql.Snowflake.SnowType=SnowType.IdWorker;

//產(chǎn)生一個(gè)唯一的訂單號(hào)

Int64orderid=HiSql.Snowflake.NextId();

//加鎖并執(zhí)行將一個(gè)訂單的批次都加鎖防止同一時(shí)間被其它業(yè)務(wù)修改

var_rtn=HiSql.Lock.LockOnExecute(grparr,()=

//能執(zhí)行到此說明已經(jīng)加鎖成功(注:非數(shù)據(jù)庫級(jí)加鎖)

DataTabledt=_sqlClient.HiSql($"selectBatch,Material,Location,st_kcfromH_StockwhereBatchin({grparr.ToSqlIn()})andst_kc0").ToTable();

if(dt.Rows.Count0)

Listobjectlstorder=newListobject

Console.WriteLine($"雪花ID{orderid}");

string_shop="4301";//門店編號(hào)

_sqlClient.BeginTran();

foreach(stringningrparr)

ints=random.Next(1,10);

intv=_sqlClient.Update("H_Stock",new{st_kc=$"`st_kc`-{s}"}).Where($"Batch='{n}'andst_kc={s}").ExecCommand();

if(v==0)

_flag=false;

Console.WriteLine($"批次:[{n}]扣減[{s}]失敗");

rtn=newTuplebool,string(false,$"批次:[{n}]庫存已經(jīng)不足");

_sqlClient.RollBackTran();

break;

else

DataRow_drow=dt.AsEnumerable().Where(s=s.Fieldstring("Batch").Equals(n)).FirstOrDefault();

if(_drow!=null)

lstorder.Add(

OrderId=orderid,

Batch=_drow["Batch"].ToString(),

Material=_drow["Material"].ToString(),

Shop=_shop,

Location=_drow["Location"].ToString(),

SalesNum=s,

else

_flag=false;

Console.WriteLine($"批次:[{n}]扣減[{s}]失敗未找到庫存");

_sqlClient.RollBackTran();

break;

if(_flag)

//生成訂單

if(lstorder.Count0)

_sqlClient.Insert("H_Order",lstorder).ExecCommand();

_sqlClient.CommitTran();

else

Console.WriteLine($"庫存不足...");

rtn=newTuplebool,string(false,"庫存已經(jīng)不足");

},newLckInfo

UName="tanar",

Ip=""

},20,10);//加鎖超時(shí)時(shí)間設(shè)定

_sqlClient.Close();

Console.WriteLine(_rtn.Item2);

//可以注釋線程等待

//Thread.Sleep(random.Next(1,10)*100);

if(rtn.Item1)

returnCreateSale(grparr);

else

returnrtn;

}

數(shù)據(jù)庫連接配置

internalclassDemo_Init

publicstaticHiSqlClientGetSqlClien

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論