关于生成并发唯一性流水号的解决方案
看了文章《弃用数据库自增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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
请看我的测试代码以及并发结果图
从上面多线程的测试效果上来说,绝对不要去按照原
作者的方法去做。
不厚道,说我只说不做,所以,我打算就再写一个切实可行的例子,供大家参考,仅仅作为抛砖引玉。
但是本人是经过多线程测试的,至少在我测试情况下
不会出现并发出差错的情况。
1、表结构和效果图,这个表是用来存储基础因子的,需要的可以拓展字段,比
如,升序,降序,起始序号等。
(PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
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
2
3
4
5
6
7
8
GetSerialNo
9
10
11
12
13
14
15
16
17
18
19
20
21
22
锁定该条记录,好多人用lock去锁,起始这里只要执行一句update就可以了
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
第一张图(左)是单独对进货单执行循环多进程
第二张图(中)是单独对发货单执行循环多进程
第三张图(右)是对进货单发货单同时执行循环多进程
也就是上面三个Thread,自己注释测试就可以了。
测试并发代码
1protected void Page_Load(object sender, EventArgs e)
2
总结:我写的整个方法和存储
过程如果要实现流水号的话,还是相当可以的。在当前测试过程中是可以避免并发而导致数据的同步性出错的情况。