关于生成并发唯一性流水号的解决方案
2011-01-20 11:13
260 查看
关于生成并发唯一性流水号的解决方案
看了文章《弃用数据库自增ID,曝光一下我自己用到的解决方法 》,居然还显示到首页上去。我却觉得如果新手不辨真假,盲目顺从,那么会造成误人子弟的事实。
首先从作者的写这篇文章的目的上讲他想实现的无非是下面目的:
1、不用自增长ID,因为自增长移植的时候不方便。
2、这个存储过程可以很高效的产生唯一性的自增长ID
从我小虎的认知上来回答:
1、对于作者的第一点,完全可以用Guid来替代自增长,或者在移植的时候,可以先去掉自增长的属性。
有的人说Guid性能比不上自增长ID,这里我们先不讨论这一点,个人认为效率问题主要体现在索引技巧上。
2、关键是作者的第二点,完全是不正确的,也是我写这篇文章的首要目的。因为这个存储过程根本就没有实现在多并发(多用户)的情况
下能真正产生唯一性的主键ID。
我们看原作者的代码:
SQL code
请看我的测试代码以及并发结果图
C# code
结果图1
从上面多线程的测试效果上来说,绝对不要去按照原作者的方法去做。
--------------------------------------------------------------------------------
本来这么晚了,我不想在写了,但是不想让别人说我不厚道,说我只说不做,所以,我打算就再写一个切实可行的例子,供大家参考,仅仅作为抛砖引玉。
但是本人是经过多线程测试的,至少在我测试情况下不会出现并发出差错的情况。
1、表结构和效果图,这个表是用来存储基础因子的,需要的可以拓展字段,比如,升序,降序,起始序号等。
SQL code
2、存储过程代码
SQL code
废话不多说了,看测试代码和效果图
第一张图(左)是单独对进货单执行循环多进程
第二张图(中)是单独对发货单执行循环多进程
第三张图(右)是对进货单发货单同时执行循环多进程
也就是上面三个Thread,自己注释测试就可以了。
测试并发代码
C# code
总结:我写的整个方法和存储过程如果要实现流水号的话,还是相当可以的。在当前测试过程中是可以避免并发而导致数据的同步性出错的情况。
请注明出处[小虎原创]:
原文
看了文章《弃用数据库自增ID,曝光一下我自己用到的解决方法 》,居然还显示到首页上去。我却觉得如果新手不辨真假,盲目顺从,那么会造成误人子弟的事实。
首先从作者的写这篇文章的目的上讲他想实现的无非是下面目的:
1、不用自增长ID,因为自增长移植的时候不方便。
2、这个存储过程可以很高效的产生唯一性的自增长ID
从我小虎的认知上来回答:
1、对于作者的第一点,完全可以用Guid来替代自增长,或者在移植的时候,可以先去掉自增长的属性。
有的人说Guid性能比不上自增长ID,这里我们先不讨论这一点,个人认为效率问题主要体现在索引技巧上。
2、关键是作者的第二点,完全是不正确的,也是我写这篇文章的首要目的。因为这个存储过程根本就没有实现在多并发(多用户)的情况
下能真正产生唯一性的主键ID。
我们看原作者的代码:
SQL code
1create procedure [dbo].[up_get_table_key] 2( 3 @table_name varchar(50), 4 @key_value int output 5) 6as 7begin 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 33end
请看我的测试代码以及并发结果图
C# code
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、表结构和效果图,这个表是用来存储基础因子的,需要的可以拓展字段,比如,升序,降序,起始序号等。
SQL code
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、存储过程代码
SQL code
1Create procedure [dbo].[GetSerialNo] 2( 3 @sCode varchar(50) 4) 5 6 as 7 8--exec GetSerialNo 9 10begin 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 76end 77 78
废话不多说了,看测试代码和效果图
第一张图(左)是单独对进货单执行循环多进程
第二张图(中)是单独对发货单执行循环多进程
第三张图(右)是对进货单发货单同时执行循环多进程
也就是上面三个Thread,自己注释测试就可以了。
测试并发代码
C# code
1protected 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
总结:我写的整个方法和存储过程如果要实现流水号的话,还是相当可以的。在当前测试过程中是可以避免并发而导致数据的同步性出错的情况。
请注明出处[小虎原创]:
原文
相关文章推荐
- 关于生成并发唯一性流水号的解决方案
- 关于生成并发唯一性流水号的解决方案
- 关于生成并发唯一性流水号的解决方案
- 今天看见虎姐写的关于生成并发唯一性流水号的解决方案
- 关于生成并发唯一性流水号的解决方案
- 关于生成并发唯一性流水号的解决方案
- 关于生成并发唯一性流水号的解决方案
- 转载:关于生成并发唯一性流水号的解决方案
- WCF关于svcutil生成关于TCP绑定出现 元数据包含无法解析的引用:“net.tcp://127.0.0.1:9999/xxxx/”的解决方案
- 关于生成订单号的解决方案PHP
- WCF关于svcutil生成关于绑定出现 元数据包含无法解析的引用的解决方案
- Unity3D-关于动态生成的Button和ScrollView冲突的解决方案
- 关于缓存穿透、缓存并发、缓存失效的解决方案
- 关于在高并发下生成订单号的策略
- 流水号生成方法——按照时间格式生成的有序唯一编码(支持并发)
- 高并发下生成订单唯一流水号的方法
- 高并发环境下生成订单唯一流水号方法:SnowFlake
- 关于myeclipse无法自动生成Deploy Location的解决方案
- 关于vs08生成解决方案慢的解决方法
- Oracle 生成流水号解决方案