主键冲突的解决方案

场景是这样的:

很多存储过程要使用一个函数生成一个自增量ID,这个自增量是我们按照自己的规则生成的。增量规则是查询当前表中的最大ID只,然后我们再生成一个比当前最大的再加1的一个增量值。

在单线程,数据量不大的时候没有任何问题。

当我们测试时使用10个线程,用程序快速的发数据的时候,就出现了主键冲突。

分析冲突原因:

假设当前表中最大值为   00000100DB100301

我们调用函数生成        00000200DB100301

在我们将00000200DB100301插入表完成之前,另一个地方同样使用了该函数,取得的最大值仍是00000100DB100301,这样又生成了00000200DB100301这样的ID值。当第二个00000200DB100301插入数据库时就出现了主键冲突。

 

对于该问题我采取的办法是:

由于很多地方都使用到了这种生成方式,而且生成的ID值在程序中十分重要。所以我没有选择去更改原有表的结构。采用新增一个表,新建两个字段。一个为int型自增ID,另一个为ID值。

每次去查找自增ID的最大值,然后为ID生成一个新的ID,再插入到数据库。同时整个过程用事务一次性提交。

 

 

具体代码:

原来的函数:

ALTER function [dbo].[MNCH_Fun_GetContractNumber]
(
 @bsflag varchar(2), --B:买 S:卖 C:撤
 @ContractType varchar(2)  -- D:委托单 C:成交单
)
returns varchar(16)
as
begin
  

 --临时用
 declare @contractNumberTemp varchar(16)
 declare @typeTemp varchar(1) --D:委托单 C:成交单
 declare @bsflagTemp varchar(1) --B:买 S:卖 C:撤
 declare @dateTemp varchar(6) --六位日期(yyMMdd)
 
 declare @isFirst int   -- 0否 1是
 
 if @ContractType = 'D'
 begin
  if not exists(select delegatecontractnumber from intradaydelegate where substring(delegatecontractnumber,9,1) = 'D')
  begin
   set @isFirst = 1   
  end
  else
  begin
   set @isFirst = 0
   select top 1 @contractNumberTemp=delegatecontractnumber from intradaydelegate where substring(delegatecontractnumber,9,1) = 'D' order by delegatecontractnumber desc
  end
 end
 else
 begin
  if not exists(select dealcontractnumber from intradaydeal where substring(dealcontractnumber,9,1) = 'C')
  begin
   set @isFirst = 1
  end
  else
  begin
   set @isFirst = 0
   select top 1 @contractNumberTemp=dealcontractnumber from intradaydeal where substring(dealcontractnumber,9,1) = 'C' order by dealcontractnumber desc
  end
 end
 
 if @isFirst = 1
 begin
  declare @year varchar(2)
  declare @month varchar(2)
  declare @day varchar(2)
  set @year  = substring(cast(datepart(year,getdate()) as varchar(4)),3,2)
  
  set @month = cast(datepart(month,getdate()) as varchar(2))
  if len(@month) = 1
  begin
   set @month = '0' + @month
  end
  
  set @day   = cast(datepart(day,getdate()) as varchar(2))
  if len(@day) = 1
  begin
   set @day = '0' + @day
  end
  set @contractNumberTemp = '00000001' + @ContractType + @bsflag + @year + @month + @day 
  
 end
 else
 begin
  declare @increaceNumberTemp varchar(8)  --递增号
  declare @nIncreaceNumberTemp int --整型递增号  
  declare @increateNumberLength int -- 委托合同号长度
  set @increateNumberLength = 8
  declare @increaceNumberNew varchar(8)  --新合同递增号    
  
  set @typeTemp = substring(@contractNumberTemp,9,1)
  set @bsflagTemp = substring(@contractNumberTemp,10,1)
  set @dateTemp = substring(@contractNumberTemp,11,6)
  set @increaceNumberTemp = substring(@contractNumberTemp,1,8)
  
  
  set @nIncreaceNumberTemp = cast(@increaceNumberTemp as int)
  set @nIncreaceNumberTemp = @nIncreaceNumberTemp + 1  --递增
  
  declare @nLen int
  set @nLen = len(cast(@nIncreaceNumberTemp as varchar))
  set @increaceNumberNew = substring(@increaceNumberTemp,1,@increateNumberLength-@nLen) + cast(@nIncreaceNumberTemp as varchar)
  set @contractNumberTemp = @increaceNumberNew + @typeTemp + @bsflag + @dateTemp
 end 
 return @contractNumberTemp 
end

 

修改后的存储过程


ALTER proc [dbo].[MNCH_Proc_GetContractNumber]
(
 @bsflag varchar(2), --B:买 S:卖 C:撤
 @ContractType varchar(2),  -- D:委托单 C:成交单
 @IndependentIncrementNum nvarchar(16) output
)
--returns varchar(16)
as
begin 
begin transaction
 --临时用 
 declare @bsflagTemp varchar(1) --B:买 S:卖 C:撤
  
 declare @isFirst int   -- 0否 1是

 if not exists(select IndependentIncrementID from IndependentIncrement)
 begin
  set @isFirst = 1   
 end
 else
 begin
  set @isFirst = 0
 end
    declare @year varchar(2)
 declare @month varchar(2)
 declare @day varchar(2)
 set @year  = substring(cast(datepart(year,getdate()) as varchar(4)),3,2)
 
 set @month = cast(datepart(month,getdate()) as varchar(2))
 if len(@month) = 1
 begin
  set @month = '0' + @month
 end
 
 set @day   = cast(datepart(day,getdate()) as varchar(2))
 if len(@day) = 1
 begin
  set @day = '0' + @day
 end
 if @isFirst = 1
 begin  
  set @IndependentIncrementNum = '00000001' + @ContractType + @bsflag + @year + @month + @day 
  insert into IndependentIncrement(IndependentIncrementNum) values(@IndependentIncrementNum)  
 end
 else
 begin 
  
  declare @tmpID int
  set @tmpID=0
  select top 1 @tmpID=IndependentIncrementID from dbo.IndependentIncrement order by IndependentIncrementID desc
  declare @tmpIDLen int
  set @tmpIDLen=len(@tmpID)
  declare @lessLen int
  set @lessLen=8-@tmpIDLen
  declare @increaceNumberNew nvarchar(8)
  if @lessLen = 0
   set @increaceNumberNew=Convert(nvarchar(8),@tmpID)
  else if @lessLen = 1
   set @increaceNumberNew='0'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 2
   set @increaceNumberNew='00'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 3
   set @increaceNumberNew='000'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 4
   set @increaceNumberNew='0000'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 5
   set @increaceNumberNew='00000'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 6
   set @increaceNumberNew='000000'+Convert(nvarchar(8),@tmpID)
  else if @lessLen = 7
   set @increaceNumberNew='0000000'+Convert(nvarchar(8),@tmpID)
  set @IndependentIncrementNum = @increaceNumberNew+ @ContractType + @bsflag + @year + @month + @day 
  insert into IndependentIncrement(IndependentIncrementNum) values(@IndependentIncrementNum)
 end  
 --return @contractNumberTemp 
commit transaction
end

经过25个线程,同时发95条记录测试,不存在主键冲突(是否有数据丢失未经测试)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值