今天看见虎姐写的关于生成并发唯一性流水号的解决方案

 http://www.52rs.net/ArticleView.aspx?gID=71bd9b1d-ad30-4f6e-896d-fed7dfbc1b3d转载。微笑

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

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

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

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) 
    { 
        if (!IsPostBack) 
        { 
            for (int i = 0; i < 100; i++) 
            { 
                System.Threading.Thread temp3 = new System.Threading.Thread(new System.Threading.ThreadStart(Run3)); 
 
                  temp3.Start(); 
 
              } 
        } 
    } 
 
     
 
      private void Run3() 
    { 
        System.Data.SqlClient.SqlParameter[] p = { 
                    new System.Data.SqlClient.SqlParameter("@table_name", "test"), 
                    new System.Data.SqlClient.SqlParameter("@key_value",System.Data.SqlDbType.Int) }; 
        p[1].Direction = System.Data.ParameterDirection.Output; 
        SqlHelper.ExecuteStoredProcedure("up_get_table_key", p); 
        Response.Write(p[1].Value.ToString() + "<br/>"); 
    } 
 

结果图1

 

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

  


 

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

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

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     { 
3         if (!IsPostBack) 
4         { 
5             for (int i = 0; i < 100; i++) 
6             { 
7                 System.Threading.Thread temp = new System.Threading.Thread(new System.Threading.ThreadStart(Run)); 
8 System.Threading.Thread temp2 = new System.Threading.Thread(new System.Threading.ThreadStart(Run2)); 
9                 System.Threading.Thread temp3 = new System.Threading.Thread(new System.Threading.ThreadStart(Run3)); 
10                 temp.Start(); 
11                 temp2.Start(); 
12                 temp3.Start(); 
13             } 
14         } 
15     } 
16  
17     private void Run() 
18     { 
19 System.Data.SqlClient.SqlParameter[] p = { 
20                     new System.Data.SqlClient.SqlParameter("@sCode", "JHD") }; 
21         Response.Write(SqlHelper.ExecuteStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "<br/>"); 
22     } 
23     private void Run2() 
24     { 
25         System.Data.SqlClient.SqlParameter[] p = { 
26                     new System.Data.SqlClient.SqlParameter("@sCode", "XSD") }; 
27         Response.Write(SqlHelper.ExecuteStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "<br/>"); 
28     } 
29     private void Run3() 
30     { 
31         System.Data.SqlClient.SqlParameter[] p = { 
32                     new System.Data.SqlClient.SqlParameter("@table_name", "test"), 
33                     new System.Data.SqlClient.SqlParameter("@key_value",System.Data.SqlDbType.Int) }; 
34         p[1].Direction = System.Data.ParameterDirection.Output; 
35         SqlHelper.ExecuteStoredProcedure("up_get_table_key", p); 
36         Response.Write(p[1].Value.ToString() + "<br/>"); 
37     } 
38

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

请注明出处[小虎原创]:http://www.52rs.net/ArticleView.aspx?gID=71bd9b1d-ad30-4f6e-896d-fed7dfbc1b3d

  

 

 

 



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值