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

看了文章《弃用数据库自增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)); 
8System.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     
19System.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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值