关于生成并发唯一性流水号的解决方案

  看了文章《弃用数据库自增ID,曝光一下我自己用到的解决方法 ,居然还显示到首页上去。我却觉得如果新手不辨真假,盲目顺从,那么会造成误人子弟的事实。

首先从作者的写这篇文章的目的上讲他想实现的无非是下面
目的:

1、不用自增长ID,因为自增长移植的时候不方便。

2、这个存储过程可以很高效的产生唯一性的自增长ID

从我小虎的认知上来回答:

1、对于作者的第一点,完全可以用Guid来替代自增长,或者在移植的时候,可以先去掉自增长的属性。有的人说Guid性能比不上自增长ID,这里我们先不讨论这一点,个
人认为效率问题主要体现在索引技巧上。

2、关键是作者的第二点,完全是不正确的,也是我写这篇文章的首要目的。因为这个存储过程根本就没有实现在多并发(多用户)的情况下能真正产生唯一性的主键ID

我们看原作者的代码:

1关于生成并发唯一性流水号的解决方案create procedure [dbo].[up_get_table_key]
2关于生成并发唯一性流水号的解决方案(
3关于生成并发唯一性流水号的解决方案   @table_name varchar(50),
4关于生成并发唯一性流水号的解决方案   @key_value int output
5关于生成并发唯一性流水号的解决方案)
6关于生成并发唯一性流水号的解决方案as
7关于生成并发唯一性流水号的解决方案begin
8关于生成并发唯一性流水号的解决方案     begin tran
9关于生成并发唯一性流水号的解决方案         declare @key int
10关于生成并发唯一性流水号的解决方案        
11关于生成并发唯一性流水号的解决方案         --initialize the key with 1
12关于生成并发唯一性流水号的解决方案 set @key=1
13关于生成并发唯一性流水号的解决方案         --whether the specified table is exist
14关于生成并发唯一性流水号的解决方案 if not exists(select table_name from table_key where table_name=@table_name)
15关于生成并发唯一性流水号的解决方案            begin
16关于生成并发唯一性流水号的解决方案              insert into table_key values(@table_name,@key)        --default key vlaue:1
17关于生成并发唯一性流水号的解决方案 end
18关于生成并发唯一性流水号的解决方案         -- step increase
19关于生成并发唯一性流水号的解决方案 else    
20关于生成并发唯一性流水号的解决方案            begin
21关于生成并发唯一性流水号的解决方案                select @key=key_value from table_key with (nolock) where table_name=@table_name
22关于生成并发唯一性流水号的解决方案                set @key=@key+1
23关于生成并发唯一性流水号的解决方案                --update the key value by table name
24关于生成并发唯一性流水号的解决方案 update table_key set key_value=@key where table_name=@table_name
25关于生成并发唯一性流水号的解决方案            end
26关于生成并发唯一性流水号的解决方案        --set ouput value
27关于生成并发唯一性流水号的解决方案 set @key_value=@key
28关于生成并发唯一性流水号的解决方案
29关于生成并发唯一性流水号的解决方案    --commit tran
30关于生成并发唯一性流水号的解决方案 commit tran
31关于生成并发唯一性流水号的解决方案        if @@error>0
32关于生成并发唯一性流水号的解决方案      rollback tran
33关于生成并发唯一性流水号的解决方案end

请看我的测试代码以及并发结果图

关于生成并发唯一性流水号的解决方案关于生成并发唯一性流水号的解决方案protected void Page_Load(object sender, EventArgs e)

从上面多线程的测试效果上来说,绝对不要去按照原
作者的方法去做。

本来这么晚了,我不想在写了,但是不想让别人说我
不厚道,说我只说不做,所以,我打算就再写一个切实可行的例子,供大家参考,仅仅作为抛砖引玉。

但是本人是经过多线程测试的,至少在我测试情况下
不会出现并发出差错的情况。

1、表结构和效果图,这个表是用来存储基础因子的,需要的可以拓展字段,比
如,升序,降序,起始序号等。

关于生成并发唯一性流水号的解决方案CREATE TABLE [dbo].[SerialNo](
关于生成并发唯一性流水号的解决方案   
[sCode] [varchar](50) NOT NULL,--主键也是多个流水号的类别区分
关于生成并发唯一性流水号的解决方案
[sName] [varchar](100) NULL,--名称,备注形式
关于生成并发唯一性流水号的解决方案
[sQZ] [varchar](50) NULL,--前缀
关于生成并发唯一性流水号的解决方案
[sValue] [varchar](80) NULL,--因子字段
关于生成并发唯一性流水号的解决方案
CONSTRAINT [PK_SerialNo] PRIMARY KEY CLUSTERED
关于生成并发唯一性流水号的解决方案(
关于生成并发唯一性流水号的解决方案   
[sCode] ASC
关于生成并发唯一性流水号的解决方案)
WITH
(PAD_INDEX 
= OFF,
STATISTICS_NORECOMPUTE 
= OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON,
关于生成并发唯一性流水号的解决方案 ALLOW_PAGE_LOCKS 
= ON) ON [PRIMARY]
关于生成并发唯一性流水号的解决方案)
ON [PRIMARY]
关于生成并发唯一性流水号的解决方案关于生成并发唯一性流水号的解决方案 
2、存储过程代码
1关于生成并发唯一性流水号的解决方案Create procedure [dbo].[GetSerialNo]  
2关于生成并发唯一性流水号的解决方案(  
3关于生成并发唯一性流水号的解决方案    @sCode varchar(50)  
4关于生成并发唯一性流水号的解决方案)  
5关于生成并发唯一性流水号的解决方案
6关于生成并发唯一性流水号的解决方案  as
7关于生成并发唯一性流水号的解决方案
8关于生成并发唯一性流水号的解决方案--exec
GetSerialNo 

9关于生成并发唯一性流水号的解决方案
10关于生成并发唯一性流水号的解决方案begin
11关于生成并发唯一性流水号的解决方案
12关于生成并发唯一性流水号的解决方案   Declare @sValue varchar(16),  
13关于生成并发唯一性流水号的解决方案
14关于生成并发唯一性流水号的解决方案           @dToday datetime,          
15关于生成并发唯一性流水号的解决方案
16关于生成并发唯一性流水号的解决方案           @sQZ varchar(50--这个代表前缀
17关于生成并发唯一性流水号的解决方案
18关于生成并发唯一性流水号的解决方案   Begin Tran    
19关于生成并发唯一性流水号的解决方案
20关于生成并发唯一性流水号的解决方案   Begin Try  
21关于生成并发唯一性流水号的解决方案
22关于生成并发唯一性流水号的解决方案     --
锁定该条记录,好多人用lock去锁,起始这里只要执行一句update就可以了

23关于生成并发唯一性流水号的解决方案 --在同一个事物中,执行了update语句之后就会启动锁
24关于生成并发唯一性流水号的解决方案 Update SerialNo set sValue=sValue where sCode=@sCode  
25关于生成并发唯一性流水号的解决方案
26关于生成并发唯一性流水号的解决方案     Select @sValue = sValue From SerialNo where sCode=@sCode  
27关于生成并发唯一性流水号的解决方案
28关于生成并发唯一性流水号的解决方案     Select @sQZ = sQZ From SerialNo where sCode=@sCode  
29关于生成并发唯一性流水号的解决方案
30关于生成并发唯一性流水号的解决方案     -- 因子表中没有记录,插入初始值 
31关于生成并发唯一性流水号的解决方案
32关于生成并发唯一性流水号的解决方案     If @sValue is null  
33关于生成并发唯一性流水号的解决方案
34关于生成并发唯一性流水号的解决方案     Begin
35关于生成并发唯一性流水号的解决方案
36关于生成并发唯一性流水号的解决方案       Select @sValue = convert(bigint, convert(varchar(6), getdate(), 12) + '000001')  
37关于生成并发唯一性流水号的解决方案
38关于生成并发唯一性流水号的解决方案       Update SerialNo set sValue=@sValue where sCode=@sCode  
39关于生成并发唯一性流水号的解决方案
40关于生成并发唯一性流水号的解决方案     end else  
41关于生成并发唯一性流水号的解决方案
42关于生成并发唯一性流水号的解决方案     Begin --因子表中没 有记录 
43关于生成并发唯一性流水号的解决方案
44关于生成并发唯一性流水号的解决方案       Select @dToday = substring(@sValue,1,6)  
45关于生成并发唯一性流水号的解决方案
46关于生成并发唯一性流水号的解决方案       --如果日期相等,则加1 
47关于生成并发唯一性流水号的解决方案
48关于生成并发唯一性流水号的解决方案       If @dToday = convert(varchar(6), getdate(), 12)  
49关于生成并发唯一性流水号的解决方案
50关于生成并发唯一性流水号的解决方案         Select @sValue = convert(varchar(16), (convert(bigint, @sValue) + 1))  
51关于生成并发唯一性流水号的解决方案
52关于生成并发唯一性流水号的解决方案       else --如果日期不 相等,则先赋值日期,流水号从1开始 
53关于生成并发唯一性流水号的解决方案
54关于生成并发唯一性流水号的解决方案         Select @sValue = convert(bigint, convert(varchar(6), getdate(), 12) +'000001')  
55关于生成并发唯一性流水号的解决方案
56关于生成并发唯一性流水号的解决方案          
57关于生成并发唯一性流水号的解决方案
58关于生成并发唯一性流水号的解决方案       Update SerialNo set sValue =@sValue where sCode=@sCode  
59关于生成并发唯一性流水号的解决方案
60关于生成并发唯一性流水号的解决方案     End
61关于生成并发唯一性流水号的解决方案
62关于生成并发唯一性流水号的解决方案     Select result = @sQZ+@sValue    
63关于生成并发唯一性流水号的解决方案
64关于生成并发唯一性流水号的解决方案     Commit Tran  
65关于生成并发唯一性流水号的解决方案
66关于生成并发唯一性流水号的解决方案   End Try  
67关于生成并发唯一性流水号的解决方案
68关于生成并发唯一性流水号的解决方案   Begin Catch  
69关于生成并发唯一性流水号的解决方案
70关于生成并发唯一性流水号的解决方案     Rollback Tran  
71关于生成并发唯一性流水号的解决方案
72关于生成并发唯一性流水号的解决方案     Select result = 'Error'
73关于生成并发唯一性流水号的解决方案
74关于生成并发唯一性流水号的解决方案   End Catch  
75关于生成并发唯一性流水号的解决方案
76关于生成并发唯一性流水号的解决方案end
77关于生成并发唯一性流水号的解决方案
78关于生成并发唯一性流水号的解决方案

废话不多说了,看测试代码和效果图

关于生成并发唯一性流水号的解决方案 关于生成并发唯一性流水号的解决方案 关于生成并发唯一性流水号的解决方案

 




















第一张图(左)是单独对进货单执行循环多进程


第二张图(中)是单独对发货单执行循环多进程


第三张图(右)是对进货单发货单同时执行循环多进程


也就是上面三个Thread,自己注释测试就可以了。

测试并发代码

1关于生成并发唯一性流水号的解决方案protected void Page_Load(object sender, EventArgs e)
2



总结:我写的整个方法和存储
过程如果要实现流水号的话,还是相当可以的。在当前测试过程中是可以避免并发而导致数据的同步性出错的情况。

相关文章: