版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2025學(xué)年山東省菏澤市高二下學(xué)期期中考試歷史試題(A)(解析版)
- 2024-2025學(xué)年江蘇省鹽城市高二下學(xué)期期終考試歷史試題(解析版)
- 2026年生物與醫(yī)學(xué)前沿科技知識(shí)競(jìng)賽題集
- 2026年計(jì)算機(jī)應(yīng)用基礎(chǔ)初級(jí)水平測(cè)試題
- 2026年心理學(xué)入門認(rèn)知心理學(xué)與社會(huì)心理學(xué)試題庫
- 2026年城市規(guī)劃領(lǐng)域?qū)I(yè)技術(shù)人員考試練習(xí)題集
- 2026年文化常識(shí)與歷史知識(shí)綜合測(cè)試題
- 2026年高考化學(xué)模擬試題及答案解析
- 2026年寫作技巧基礎(chǔ)訓(xùn)練初級(jí)自測(cè)模擬題
- 2026年房地產(chǎn)銷售經(jīng)理人才選拔模擬測(cè)試
- 2025-2026學(xué)年北京市西城區(qū)高三(上期)期末考試地理試卷(含答案詳解)
- 贛州市章貢區(qū)2026年社區(qū)工作者(專職網(wǎng)格員)招聘【102人】考試參考題庫及答案解析
- 江蘇高職單招培訓(xùn)課件
- 2026年山東理工職業(yè)學(xué)院?jiǎn)握芯C合素質(zhì)考試參考題庫帶答案解析
- 2026年及未來5年市場(chǎng)數(shù)據(jù)中國氟樹脂行業(yè)發(fā)展?jié)摿Ψ治黾巴顿Y方向研究報(bào)告
- DB1331∕T 109-2025 雄安新區(qū)建設(shè)工程抗震設(shè)防標(biāo)準(zhǔn)
- DB37∕T 1317-2025 超細(xì)干粉滅火系統(tǒng)技術(shù)規(guī)范
- Scratch講座課件教學(xué)課件
- 《低碳醫(yī)院評(píng)價(jià)指南》(T-SHWSHQ 14-2025)
- 2025至2030中國砷化鎵太陽能電池外延片行業(yè)市場(chǎng)深度研究與戰(zhàn)略咨詢分析報(bào)告
- 質(zhì)量環(huán)境及職業(yè)健康安全三體系風(fēng)險(xiǎn)和機(jī)遇識(shí)別評(píng)價(jià)分析及控制措施表(包含氣候變化)
評(píng)論
0/150
提交評(píng)論